From 95f1e7cc80834dd04b8db482cbfc1b21b3e28a22 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Mon, 27 Feb 2017 20:17:04 -0800 Subject: [PATCH 1/2] Add SSL enabled WiFiServer, Updater, WebServer Adds SSL server mode for WiFiServerSecure, for plain SSL connections, ESP8266WebServerSecure, for HTTPS web serving, and SecureHTTPSUpdater for encrypted OTA updates. Example code is provided for all new options, as well as a BASH script for generating their own, self-signed certificates. Both ESP8266WebServerSecure and SecureHTTPSUpdater are important for secure password-based authentication. HTTP Basic Authentication, the only supported model presently, sends the username and password in *cleartext* and therefore should *never* be used in any un-SSL encrypted channel unless you don't mind sharing your login and password with anyone else on the internet. Even if the ESP8266 is not safety critical, this cleartext broadcast could expose you should you reuse this password elsewhere on your network or the internet. --- .../SecureHTTPSUpdater/SecureHTTPSUpdater.ino | 188 ++++++++++++++++ .../HelloServerSecure/HelloServerSecure.ino | 179 +++++++++++++++ libraries/ESP8266WebServer/keywords.txt | 1 + .../ESP8266WebServer/src/ESP8266WebServer.cpp | 15 +- .../ESP8266WebServer/src/ESP8266WebServer.h | 12 +- .../src/ESP8266WebServerSecure.cpp | 140 ++++++++++++ .../src/ESP8266WebServerSecure.h | 55 +++++ .../WiFiHTTPSServer/WiFiHTTPSServer.ino | 204 ++++++++++++++++++ .../WiFiHTTPSServer/make-self-signed-cert.sh | 49 +++++ libraries/ESP8266WiFi/keywords.txt | 1 + libraries/ESP8266WiFi/src/ESP8266WiFi.h | 1 + libraries/ESP8266WiFi/src/WiFiClient.h | 2 +- .../ESP8266WiFi/src/WiFiClientSecure.cpp | 101 +++++++-- libraries/ESP8266WiFi/src/WiFiClientSecure.h | 5 + libraries/ESP8266WiFi/src/WiFiServer.h | 3 +- .../ESP8266WiFi/src/WiFiServerSecure.cpp | 79 +++++++ libraries/ESP8266WiFi/src/WiFiServerSecure.h | 43 ++++ 17 files changed, 1049 insertions(+), 29 deletions(-) create mode 100644 libraries/ESP8266HTTPUpdateServer/examples/SecureHTTPSUpdater/SecureHTTPSUpdater.ino create mode 100644 libraries/ESP8266WebServer/examples/HelloServerSecure/HelloServerSecure.ino create mode 100644 libraries/ESP8266WebServer/src/ESP8266WebServerSecure.cpp create mode 100644 libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h create mode 100644 libraries/ESP8266WiFi/examples/WiFiHTTPSServer/WiFiHTTPSServer.ino create mode 100755 libraries/ESP8266WiFi/examples/WiFiHTTPSServer/make-self-signed-cert.sh create mode 100644 libraries/ESP8266WiFi/src/WiFiServerSecure.cpp create mode 100644 libraries/ESP8266WiFi/src/WiFiServerSecure.h diff --git a/libraries/ESP8266HTTPUpdateServer/examples/SecureHTTPSUpdater/SecureHTTPSUpdater.ino b/libraries/ESP8266HTTPUpdateServer/examples/SecureHTTPSUpdater/SecureHTTPSUpdater.ino new file mode 100644 index 0000000000..c536ad1367 --- /dev/null +++ b/libraries/ESP8266HTTPUpdateServer/examples/SecureHTTPSUpdater/SecureHTTPSUpdater.ino @@ -0,0 +1,188 @@ +/* + SecureHTTPSUpdater - SSL encrypted, password-protected firmware update + + This example starts a HTTPS server on the ESP8266 to allow firmware updates + to be performed. All communication, including the username and password, + is encrypted via SSL. Be sure to update the SSID and PASSWORD before running + to allow connection to your WiFi network. + + IMPORTANT NOTES ABOUT SSL CERTIFICATES + + 1. USE/GENERATE YOUR OWN CERTIFICATES + While a sample, self-signed certificate is included in this example, + it is ABSOLUTELY VITAL that you use your own SSL certificate in any + real-world deployment. Anyone with the certificate and key may be + able to decrypt your traffic, so your own keys should be kept in a + safe manner, not accessible on any public network. + + 2. HOW TO GENERATE YOUR OWN CERTIFICATE/KEY PAIR + A sample script, "make-self-signed-cert.sh" is provided in the + ESP8266WiFi/examples/WiFiHTTPSServer directory. This script can be + modified (replace "your-name-here" with your Organization name). Note + that this will be a *self-signed certificate* and will *NOT* be accepted + by default by most modern browsers. They'll display something like, + "This certificate is from an untrusted source," or "Your connection is + not secure," or "Your connection is not private," and the user will + have to manully allow the browser to continue by using the + "Advanced/Add Exception" (FireFox) or "Advanced/Proceed" (Chrome) link. + + You may also, of course, use a commercial, trusted SSL provider to + generate your certificate. When requesting the certificate, you'll + need to specify that it use SHA256 and 1024 or 512 bits in order to + function with the axTLS implementation in the ESP8266. + + Interactive usage: + Go to https://esp8266-webupdate.local/firmware, enter the username + and password, and the select a new BIN to upload. + + To upload through terminal you can use: + curl -u admin:admin -F "image=@firmware.bin" esp8266-webupdate.local/firmware + + Adapted by Earle F. Philhower, III, from the SecureWebUpdater.ino example. + This example is released into the public domain. +*/ + +#include +#include +#include +#include +#include + +const char* host = "esp8266-webupdate"; +const char* update_path = "/firmware"; +const char* update_username = "admin"; +const char* update_password = "admin"; +const char* ssid = "........"; +const char* password = "........"; + +ESP8266WebServerSecure httpServer(443); +ESP8266HTTPUpdateServer httpUpdater; + +// The certificate is stored in PMEM +static const uint8_t x509[] PROGMEM = { + 0x30, 0x82, 0x01, 0xc9, 0x30, 0x82, 0x01, 0x32, 0x02, 0x09, 0x00, 0xe6, + 0x60, 0x8d, 0xa3, 0x47, 0x8f, 0x57, 0x7a, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x29, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0a, 0x70, + 0x73, 0x79, 0x63, 0x68, 0x6f, 0x70, 0x6c, 0x75, 0x67, 0x31, 0x12, 0x30, + 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, + 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, + 0x32, 0x32, 0x34, 0x30, 0x38, 0x30, 0x35, 0x33, 0x36, 0x5a, 0x17, 0x0d, + 0x33, 0x30, 0x31, 0x31, 0x30, 0x33, 0x30, 0x38, 0x30, 0x35, 0x33, 0x36, + 0x5a, 0x30, 0x29, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x0c, 0x0a, 0x70, 0x73, 0x79, 0x63, 0x68, 0x6f, 0x70, 0x6c, 0x75, 0x67, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x31, + 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9f, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, + 0x00, 0xb6, 0x59, 0xd0, 0x57, 0xbc, 0x3e, 0xb9, 0xa0, 0x6c, 0xf5, 0xd5, + 0x46, 0x49, 0xaa, 0x9a, 0xb3, 0xbf, 0x09, 0xa9, 0xbb, 0x82, 0x3b, 0xdf, + 0xb7, 0xe3, 0x5a, 0x8e, 0x31, 0xf7, 0x27, 0xdf, 0xaa, 0xed, 0xa3, 0xd6, + 0xf6, 0x74, 0x35, 0xfc, 0x8d, 0x0b, 0xbc, 0xa2, 0x96, 0x10, 0x57, 0xe8, + 0xb2, 0xaa, 0x94, 0xf2, 0x47, 0x12, 0x4e, 0x3f, 0x7c, 0x5e, 0x90, 0xfe, + 0xad, 0x75, 0x88, 0xca, 0x7b, 0x9a, 0x18, 0x15, 0xbe, 0x3d, 0xe0, 0x31, + 0xb5, 0x45, 0x7f, 0xe7, 0x9d, 0x22, 0x99, 0x65, 0xba, 0x63, 0x70, 0x81, + 0x3b, 0x37, 0x22, 0x97, 0x64, 0xc5, 0x57, 0x8c, 0x98, 0x9c, 0x10, 0x36, + 0x98, 0xf0, 0x0b, 0x19, 0x28, 0x16, 0x9a, 0x40, 0x31, 0x5f, 0xbc, 0xd9, + 0x8e, 0x73, 0x68, 0xe1, 0x6a, 0x5d, 0x91, 0x0b, 0x4f, 0x73, 0xa4, 0x6b, + 0x8f, 0xa5, 0xad, 0x12, 0x09, 0x32, 0xa7, 0x66, 0x3b, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x1b, 0x46, 0x78, + 0xd1, 0xfa, 0x21, 0xc1, 0xd6, 0x75, 0xc0, 0x83, 0x59, 0x57, 0x05, 0xd5, + 0xae, 0xf8, 0x8c, 0x78, 0x03, 0x65, 0x3b, 0xbf, 0xef, 0x70, 0x3f, 0x78, + 0xc6, 0xe1, 0x5a, 0xac, 0xb1, 0x93, 0x5b, 0x41, 0x35, 0x45, 0x47, 0xf8, + 0x07, 0x86, 0x40, 0x34, 0xa2, 0x9e, 0x2a, 0x16, 0x8d, 0xea, 0xf9, 0x1e, + 0x1f, 0xd7, 0x70, 0xb4, 0x28, 0x6b, 0xd8, 0xf5, 0x3f, 0x33, 0x3f, 0xc2, + 0x2c, 0x69, 0xf2, 0xa3, 0x54, 0x4d, 0xbf, 0x7d, 0xf9, 0xde, 0x05, 0x0c, + 0x9c, 0xe3, 0x1b, 0x72, 0x07, 0x7b, 0x41, 0x76, 0x1a, 0x57, 0x03, 0x5d, + 0xb2, 0xff, 0x4c, 0x17, 0xbd, 0xd7, 0x73, 0x32, 0x98, 0x26, 0x6b, 0x2c, + 0xc4, 0xbf, 0x6e, 0x01, 0x36, 0x8b, 0xbf, 0x00, 0x48, 0x9c, 0xfb, 0x3d, + 0x7d, 0x76, 0x1f, 0x55, 0x96, 0x43, 0xc5, 0x4e, 0xc1, 0xa3, 0xa1, 0x6a, + 0x94, 0x5f, 0x84, 0x3a, 0xdd +}; + +// And so is the key. These could also be in DRAM +static const uint8_t rsakey[] PROGMEM = { + 0x30, 0x82, 0x02, 0x5c, 0x02, 0x01, 0x00, 0x02, 0x81, 0x81, 0x00, 0xb6, + 0x59, 0xd0, 0x57, 0xbc, 0x3e, 0xb9, 0xa0, 0x6c, 0xf5, 0xd5, 0x46, 0x49, + 0xaa, 0x9a, 0xb3, 0xbf, 0x09, 0xa9, 0xbb, 0x82, 0x3b, 0xdf, 0xb7, 0xe3, + 0x5a, 0x8e, 0x31, 0xf7, 0x27, 0xdf, 0xaa, 0xed, 0xa3, 0xd6, 0xf6, 0x74, + 0x35, 0xfc, 0x8d, 0x0b, 0xbc, 0xa2, 0x96, 0x10, 0x57, 0xe8, 0xb2, 0xaa, + 0x94, 0xf2, 0x47, 0x12, 0x4e, 0x3f, 0x7c, 0x5e, 0x90, 0xfe, 0xad, 0x75, + 0x88, 0xca, 0x7b, 0x9a, 0x18, 0x15, 0xbe, 0x3d, 0xe0, 0x31, 0xb5, 0x45, + 0x7f, 0xe7, 0x9d, 0x22, 0x99, 0x65, 0xba, 0x63, 0x70, 0x81, 0x3b, 0x37, + 0x22, 0x97, 0x64, 0xc5, 0x57, 0x8c, 0x98, 0x9c, 0x10, 0x36, 0x98, 0xf0, + 0x0b, 0x19, 0x28, 0x16, 0x9a, 0x40, 0x31, 0x5f, 0xbc, 0xd9, 0x8e, 0x73, + 0x68, 0xe1, 0x6a, 0x5d, 0x91, 0x0b, 0x4f, 0x73, 0xa4, 0x6b, 0x8f, 0xa5, + 0xad, 0x12, 0x09, 0x32, 0xa7, 0x66, 0x3b, 0x02, 0x03, 0x01, 0x00, 0x01, + 0x02, 0x81, 0x81, 0x00, 0xa8, 0x55, 0xf9, 0x33, 0x45, 0x20, 0x52, 0x94, + 0x7a, 0x81, 0xe6, 0xc4, 0xe0, 0x34, 0x92, 0x63, 0xe4, 0xb3, 0xb2, 0xf0, + 0xda, 0xa5, 0x13, 0x3d, 0xda, 0xb0, 0x3a, 0x1c, 0x7e, 0x21, 0x5d, 0x25, + 0x9a, 0x03, 0x69, 0xea, 0x52, 0x15, 0x94, 0x73, 0x50, 0xa6, 0x6f, 0x21, + 0x41, 0x2d, 0x26, 0x2f, 0xe9, 0xb1, 0x5e, 0x87, 0xa5, 0xaa, 0x7e, 0x88, + 0xfd, 0x73, 0xb4, 0xe7, 0xc4, 0x5c, 0xe7, 0x2d, 0xeb, 0x9e, 0x6b, 0xe1, + 0xf1, 0x38, 0x45, 0xf4, 0x10, 0x12, 0xac, 0x79, 0x40, 0x72, 0xf0, 0x45, + 0x89, 0x5c, 0x9d, 0x8b, 0x7b, 0x5d, 0x69, 0xd9, 0x11, 0xf9, 0x25, 0xff, + 0xe1, 0x2a, 0xb3, 0x6d, 0x49, 0x18, 0x8d, 0x38, 0x0a, 0x6f, 0x0f, 0xbd, + 0x48, 0xd0, 0xdd, 0xcb, 0x41, 0x5c, 0x2a, 0x75, 0xa0, 0x51, 0x43, 0x4a, + 0x0b, 0xf6, 0xa2, 0xd2, 0xe9, 0xda, 0x37, 0xca, 0x2d, 0xd7, 0x22, 0x01, + 0x02, 0x41, 0x00, 0xe7, 0x11, 0xea, 0x93, 0xf4, 0x0b, 0xe6, 0xa0, 0x1a, + 0x57, 0x2d, 0xee, 0x96, 0x05, 0x5c, 0xa1, 0x08, 0x8f, 0x9c, 0xac, 0x9a, + 0x72, 0x60, 0x5a, 0x41, 0x2a, 0x92, 0x38, 0x36, 0xa5, 0xfe, 0xb9, 0x35, + 0xb2, 0x06, 0xbb, 0x02, 0x58, 0xc8, 0x93, 0xd6, 0x09, 0x6f, 0x57, 0xd7, + 0xc1, 0x2e, 0x90, 0xb3, 0x09, 0xdd, 0x0c, 0x63, 0x99, 0x91, 0xb7, 0xe4, + 0xcc, 0x6f, 0x78, 0x24, 0xbc, 0x3b, 0x7b, 0x02, 0x41, 0x00, 0xca, 0x06, + 0x4a, 0x09, 0x36, 0x08, 0xaa, 0x27, 0x08, 0x91, 0x86, 0xc5, 0x17, 0x14, + 0x6e, 0x24, 0x9a, 0x86, 0xd1, 0xbc, 0x41, 0xb1, 0x42, 0x5e, 0xe8, 0x80, + 0x5a, 0x8f, 0x7c, 0x9b, 0xe8, 0xcc, 0x28, 0xe1, 0xa2, 0x8f, 0xe9, 0xdc, + 0x60, 0xd5, 0x00, 0x34, 0x76, 0x32, 0x36, 0x00, 0x93, 0x69, 0x6b, 0xab, + 0xc6, 0x8b, 0x70, 0x95, 0x4e, 0xc2, 0x27, 0x4a, 0x24, 0x73, 0xbf, 0xcd, + 0x24, 0x41, 0x02, 0x40, 0x40, 0x46, 0x75, 0x90, 0x0e, 0x54, 0xb9, 0x24, + 0x53, 0xef, 0x68, 0x31, 0x73, 0xbd, 0xae, 0x14, 0x85, 0x43, 0x1d, 0x7b, + 0xcd, 0xc2, 0x7f, 0x16, 0xdc, 0x05, 0xb1, 0x82, 0xbd, 0x80, 0xd3, 0x28, + 0x45, 0xcd, 0x6d, 0x9d, 0xdb, 0x7b, 0x42, 0xe0, 0x0c, 0xab, 0xb7, 0x33, + 0x22, 0x2a, 0xf4, 0x7e, 0xff, 0xae, 0x80, 0xb4, 0x8f, 0x88, 0x0a, 0x46, + 0xb2, 0xf8, 0x43, 0x11, 0x92, 0x76, 0x61, 0xbd, 0x02, 0x40, 0x5c, 0x86, + 0x3a, 0xdc, 0x33, 0x1a, 0x0e, 0xcb, 0xa7, 0xb9, 0xf6, 0xae, 0x47, 0x5e, + 0xbc, 0xff, 0x18, 0xa2, 0x8c, 0x66, 0x1a, 0xf4, 0x13, 0x00, 0xa2, 0x9d, + 0x3e, 0x5c, 0x9e, 0xe6, 0x4c, 0xdd, 0x4c, 0x0f, 0xe2, 0xc2, 0xe4, 0x89, + 0x60, 0xf3, 0xcc, 0x8f, 0x3a, 0x5e, 0xce, 0xaa, 0xbe, 0xd8, 0xb6, 0x4e, + 0x4a, 0xb5, 0x4c, 0x0f, 0xa5, 0xad, 0x78, 0x0f, 0x15, 0xd8, 0xc9, 0x4c, + 0x2b, 0xc1, 0x02, 0x40, 0x4e, 0xe9, 0x78, 0x48, 0x94, 0x11, 0x75, 0xc1, + 0xa2, 0xc7, 0xff, 0xf0, 0x73, 0xa2, 0x93, 0xd7, 0x67, 0xc7, 0xf8, 0x96, + 0xac, 0x15, 0xaa, 0xe5, 0x5d, 0x18, 0x18, 0x29, 0xa9, 0x9a, 0xfc, 0xac, + 0x48, 0x4d, 0xa0, 0xca, 0xa2, 0x34, 0x09, 0x7c, 0x13, 0x22, 0x4c, 0xfc, + 0x31, 0x75, 0xa0, 0x21, 0x1e, 0x7a, 0x91, 0xbc, 0xb1, 0x97, 0xde, 0x43, + 0xe1, 0x40, 0x2b, 0xe3, 0xbd, 0x98, 0x44, 0xad +}; + +void setup() +{ + + Serial.begin(115200); + Serial.println(); + Serial.println("Booting Sketch..."); + WiFi.mode(WIFI_AP_STA); + WiFi.begin(ssid, password); + + while(WiFi.waitForConnectResult() != WL_CONNECTED){ + WiFi.begin(ssid, password); + Serial.println("WiFi failed, retrying."); + } + + MDNS.begin(host); + + httpServer.setServerKeyAndCert_P(rsakey, sizeof(rsakey), x509, sizeof(x509)); + httpUpdater.setup(&httpServer, update_path, update_username, update_password); + httpServer.begin(); + + MDNS.addService("https", "tcp", 443); + Serial.printf("HTTPSUpdateServer ready!\nOpen https://%s.local%s in "\ + "your browser and login with username '%s' and password "\ + "'%s'\n", host, update_path, update_username, update_password); +} + +void loop() +{ + httpServer.handleClient(); +} diff --git a/libraries/ESP8266WebServer/examples/HelloServerSecure/HelloServerSecure.ino b/libraries/ESP8266WebServer/examples/HelloServerSecure/HelloServerSecure.ino new file mode 100644 index 0000000000..44e527eceb --- /dev/null +++ b/libraries/ESP8266WebServer/examples/HelloServerSecure/HelloServerSecure.ino @@ -0,0 +1,179 @@ +/* + HelloServerSecure - Simple HTTPS server example + + This example demonstrates a basic ESP8266WebServerSecure HTTPS server + that can serve "/" and "/inline" and generate detailed 404 (not found) + HTTP respoinses. Be sure to update the SSID and PASSWORD before running + to allow connection to your WiFi network. + + IMPORTANT NOTES ABOUT SSL CERTIFICATES + + 1. USE/GENERATE YOUR OWN CERTIFICATES + While a sample, self-signed certificate is included in this example, + it is ABSOLUTELY VITAL that you use your own SSL certificate in any + real-world deployment. Anyone with the certificate and key may be + able to decrypt your traffic, so your own keys should be kept in a + safe manner, not accessible on any public network. + + 2. HOW TO GENERATE YOUR OWN CERTIFICATE/KEY PAIR + A sample script, "make-self-signed-cert.sh" is provided in the + ESP8266WiFi/examples/WiFiHTTPSServer directory. This script can be + modified (replace "your-name-here" with your Organization name). Note + that this will be a *self-signed certificate* and will *NOT* be accepted + by default by most modern browsers. They'll display something like, + "This certificate is from an untrusted source," or "Your connection is + not secure," or "Your connection is not private," and the user will + have to manully allow the browser to continue by using the + "Advanced/Add Exception" (FireFox) or "Advanced/Proceed" (Chrome) link. + + You may also, of course, use a commercial, trusted SSL provider to + generate your certificate. When requesting the certificate, you'll + need to specify that it use SHA256 and 1024 or 512 bits in order to + function with the axTLS implementation in the ESP8266. + + Interactive usage: + Go to https://esp8266-webupdate.local/firmware, enter the username + and password, and the select a new BIN to upload. + + To upload through terminal you can use: + curl -u admin:admin -F "image=@firmware.bin" esp8266-webupdate.local/firmware + + Adapted by Earle F. Philhower, III, from the HelloServer.ino example. + This example is released into the public domain. +*/ +#include +#include +#include +#include + +const char* ssid = "........"; +const char* password = "........"; + +ESP8266WebServerSecure server(443); + +// The certificate is stored in PMEM +static const uint8_t x509[] PROGMEM = { + 0x30, 0x82, 0x01, 0x3d, 0x30, 0x81, 0xe8, 0x02, 0x09, 0x00, 0xfe, 0x56, + 0x46, 0xf2, 0x78, 0xc6, 0x51, 0x17, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x26, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x45, 0x53, + 0x50, 0x38, 0x32, 0x36, 0x36, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, + 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x31, 0x38, 0x31, + 0x34, 0x34, 0x39, 0x31, 0x38, 0x5a, 0x17, 0x0d, 0x33, 0x30, 0x31, 0x31, + 0x32, 0x35, 0x31, 0x34, 0x34, 0x39, 0x31, 0x38, 0x5a, 0x30, 0x26, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x45, 0x53, + 0x50, 0x38, 0x32, 0x36, 0x36, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, + 0x31, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, + 0x41, 0x00, 0xc6, 0x72, 0x6c, 0x12, 0xe1, 0x20, 0x4d, 0x10, 0x0c, 0xf7, + 0x3a, 0x2a, 0x5a, 0x49, 0xe2, 0x2d, 0xc9, 0x7a, 0x63, 0x1d, 0xef, 0xc6, + 0xbb, 0xa3, 0xd6, 0x6f, 0x59, 0xcb, 0xd5, 0xf6, 0xbe, 0x34, 0x83, 0x33, + 0x50, 0x80, 0xec, 0x49, 0x63, 0xbf, 0xee, 0x59, 0x94, 0x67, 0x8b, 0x8d, + 0x81, 0x85, 0x23, 0x24, 0x06, 0x52, 0x76, 0x55, 0x9d, 0x18, 0x09, 0xb3, + 0x3c, 0x10, 0x40, 0x05, 0x01, 0xf3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x41, 0x00, 0x69, 0xdc, 0x6c, 0x9b, 0xa7, 0x62, 0x57, + 0x7e, 0x03, 0x01, 0x45, 0xad, 0x9a, 0x83, 0x90, 0x3a, 0xe7, 0xdf, 0xe8, + 0x8f, 0x46, 0x00, 0xd3, 0x5f, 0x2b, 0x0a, 0xde, 0x92, 0x1b, 0xc5, 0x04, + 0xc5, 0xc0, 0x76, 0xf4, 0xf6, 0x08, 0x36, 0x97, 0x27, 0x82, 0xf1, 0x60, + 0x76, 0xc2, 0xcd, 0x67, 0x6c, 0x4b, 0x6c, 0xca, 0xfd, 0x97, 0xfd, 0x33, + 0x9e, 0x12, 0x67, 0x6b, 0x98, 0x7e, 0xd5, 0x80, 0x8f +}; + +// And so is the key. These could also be in DRAM +static const uint8_t rsakey[] PROGMEM = { + 0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00, 0xc6, 0x72, + 0x6c, 0x12, 0xe1, 0x20, 0x4d, 0x10, 0x0c, 0xf7, 0x3a, 0x2a, 0x5a, 0x49, + 0xe2, 0x2d, 0xc9, 0x7a, 0x63, 0x1d, 0xef, 0xc6, 0xbb, 0xa3, 0xd6, 0x6f, + 0x59, 0xcb, 0xd5, 0xf6, 0xbe, 0x34, 0x83, 0x33, 0x50, 0x80, 0xec, 0x49, + 0x63, 0xbf, 0xee, 0x59, 0x94, 0x67, 0x8b, 0x8d, 0x81, 0x85, 0x23, 0x24, + 0x06, 0x52, 0x76, 0x55, 0x9d, 0x18, 0x09, 0xb3, 0x3c, 0x10, 0x40, 0x05, + 0x01, 0xf3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x35, 0x0b, 0x74, + 0xd3, 0xff, 0x15, 0x51, 0x44, 0x0f, 0x13, 0x2e, 0x9b, 0x0f, 0x93, 0x5c, + 0x3f, 0xfc, 0xf1, 0x17, 0xf9, 0x72, 0x94, 0x5e, 0xa7, 0xc6, 0xb3, 0xf0, + 0xfe, 0xc9, 0x6c, 0xb1, 0x1e, 0x83, 0xb3, 0xc6, 0x45, 0x3a, 0x25, 0x60, + 0x7c, 0x3d, 0x92, 0x7d, 0x53, 0xec, 0x49, 0x8d, 0xb5, 0x45, 0x10, 0x99, + 0x9b, 0xc6, 0x22, 0x3a, 0x68, 0xc7, 0x13, 0x4e, 0xb6, 0x04, 0x61, 0x21, + 0x01, 0x02, 0x21, 0x00, 0xea, 0x8c, 0x21, 0xd4, 0x7f, 0x3f, 0xb6, 0x91, + 0xfa, 0xf8, 0xb9, 0x2d, 0xcb, 0x36, 0x36, 0x02, 0x5f, 0xf0, 0x0c, 0x6e, + 0x87, 0xaa, 0x5c, 0x14, 0xf6, 0x56, 0x8e, 0x12, 0x92, 0x25, 0xde, 0xb3, + 0x02, 0x21, 0x00, 0xd8, 0x99, 0x01, 0xf1, 0x04, 0x0b, 0x98, 0xa3, 0x71, + 0x56, 0x1d, 0xea, 0x6f, 0x45, 0xd1, 0x36, 0x70, 0x76, 0x8b, 0xab, 0x69, + 0x30, 0x58, 0x9c, 0xe0, 0x45, 0x97, 0xe7, 0xb6, 0xb5, 0xef, 0xc1, 0x02, + 0x21, 0x00, 0xa2, 0x01, 0x06, 0xc0, 0xf2, 0xdf, 0xbc, 0x28, 0x1a, 0xb4, + 0xbf, 0x9b, 0x5c, 0xd8, 0x65, 0xf7, 0xbf, 0xf2, 0x5b, 0x73, 0xe0, 0xeb, + 0x0f, 0xcd, 0x3e, 0xd5, 0x4c, 0x2e, 0x91, 0x99, 0xec, 0xb7, 0x02, 0x20, + 0x4b, 0x9d, 0x46, 0xd7, 0x3c, 0x01, 0x4c, 0x5d, 0x2a, 0xb0, 0xd4, 0xaa, + 0xc6, 0x03, 0xca, 0xa0, 0xc5, 0xac, 0x2c, 0xe0, 0x3f, 0x4d, 0x98, 0x71, + 0xd3, 0xbd, 0x97, 0xe5, 0x55, 0x9c, 0xb8, 0x41, 0x02, 0x20, 0x02, 0x42, + 0x9f, 0xd1, 0x06, 0x35, 0x3b, 0x42, 0xf5, 0x64, 0xaf, 0x6d, 0xbf, 0xcd, + 0x2c, 0x3a, 0xcd, 0x0a, 0x9a, 0x4d, 0x7c, 0xad, 0x29, 0xd6, 0x36, 0x57, + 0xd5, 0xdf, 0x34, 0xeb, 0x26, 0x03 +}; + +const int led = 13; + +void handleRoot() { + digitalWrite(led, 1); + server.send(200, "text/plain", "Hello from esp8266 over HTTPS!"); + digitalWrite(led, 0); +} + +void handleNotFound(){ + digitalWrite(led, 1); + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += (server.method() == HTTP_GET)?"GET":"POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + for (uint8_t i=0; i size_t streamFile(T &file, const String& contentType){ } protected: + virtual size_t _currentClientWrite(const char* b, size_t l) { return _currentClient.write( b, l ); } + virtual size_t _currentClientWrite_P(PGM_P b, size_t l) { return _currentClient.write_P( b, l ); } void _addRequestHandler(RequestHandler* handler); void _handleRequest(); void _finalizeResponse(); diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.cpp new file mode 100644 index 0000000000..7b860c6e27 --- /dev/null +++ b/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.cpp @@ -0,0 +1,140 @@ +/* + ESP8266WebServerSecure.cpp - Dead simple HTTPS web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + + +#include +#include +#include "WiFiServer.h" +#include "WiFiClient.h" +#include "ESP8266WebServerSecure.h" + + +ESP8266WebServerSecure::ESP8266WebServerSecure(IPAddress addr, int port) +: _serverSecure(addr, port) +{ +} + +ESP8266WebServerSecure::ESP8266WebServerSecure(int port) +: _serverSecure(port) +{ +} + +void ESP8266WebServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) +{ + _serverSecure.setServerKeyAndCert_P(key, keyLen, cert, certLen); +} + +void ESP8266WebServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) +{ + _serverSecure.setServerKeyAndCert(key, keyLen, cert, certLen); +} + +ESP8266WebServerSecure::~ESP8266WebServerSecure() { + // Nothing to do here. + // Base class's destructor will be called to clean up itself +} + +// We need to basically cut-n-paste these from WebServer because of the problem +// of object slicing. The class uses assignment operators like "WiFiClient x=y;" +// When this happens, even if "y" is a WiFiClientSecure, the main class is +// already compiled down into code which will only copy the WiFiClient superclass +// and not the extra bits for our own class (since when it was compiled it needed +// to know the size of memory to allocate on the stack for this local variable +// there's not realy anything else it could do). + +void ESP8266WebServerSecure::begin() { + _currentStatus = HC_NONE; + _serverSecure.begin(); + if(!_headerKeysCount) + collectHeaders(0, 0); +} + +void ESP8266WebServerSecure::handleClient() { + if (_currentStatus == HC_NONE) { + WiFiClientSecure client = _serverSecure.available(); + if (!client) { + return; + } + +#ifdef DEBUG_ESP_HTTP_SERVER + DEBUG_OUTPUT.println("New secure client"); +#endif + + _currentClientSecure = client; + _currentStatus = HC_WAIT_READ; + _statusChange = millis(); + } + + bool keepCurrentClient = false; + bool callYield = false; + + if (_currentClientSecure.connected()) { + switch (_currentStatus) { + case HC_NONE: + // No-op to avoid C++ compiler warning + break; + case HC_WAIT_READ: + // Wait for data from client to become available + if (_currentClientSecure.available()) { + if (_parseRequest(_currentClientSecure)) { + _currentClientSecure.setTimeout(HTTP_MAX_SEND_WAIT); + _contentLength = CONTENT_LENGTH_NOT_SET; + _handleRequest(); + + if (_currentClientSecure.connected()) { + _currentStatus = HC_WAIT_CLOSE; + _statusChange = millis(); + keepCurrentClient = true; + } + } + } else { // !_currentClient.available() + if (millis() - _statusChange <= HTTP_MAX_DATA_WAIT) { + keepCurrentClient = true; + } + callYield = true; + } + break; + case HC_WAIT_CLOSE: + // Wait for client to close the connection + if (millis() - _statusChange <= HTTP_MAX_CLOSE_WAIT) { + keepCurrentClient = true; + callYield = true; + } + } + } + + if (!keepCurrentClient) { + _currentClientSecure = WiFiClientSecure(); + _currentStatus = HC_NONE; + _currentUpload.reset(); + } + + if (callYield) { + yield(); + } +} + +void ESP8266WebServerSecure::close() { + _currentClientSecure.stop(); + _serverSecure.close(); +} + diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h b/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h new file mode 100644 index 0000000000..ec6c344a48 --- /dev/null +++ b/libraries/ESP8266WebServer/src/ESP8266WebServerSecure.h @@ -0,0 +1,55 @@ +/* + ESP8266WebServerSecure.h - Dead simple HTTPS web-server. + Supports only one simultaneous client, knows how to handle GET and POST. + + Copyright (c) 2017 Earle F. Philhower, III. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + + +#ifndef ESP8266WEBSERVERSECURE_H +#define ESP8266WEBSERVERSECURE_H + +#include +#include + +class ESP8266WebServerSecure : public ESP8266WebServer +{ +public: + ESP8266WebServerSecure(IPAddress addr, int port = 443); + ESP8266WebServerSecure(int port = 443); + virtual ~ESP8266WebServerSecure(); + + void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + + WiFiClient client() override { return _currentClientSecure; } + + void begin() override; + void handleClient() override; + void close() override; + +private: + size_t _currentClientWrite (const char *bytes, size_t len) override { return _currentClientSecure.write((const uint8_t *)bytes, len); } + size_t _currentClientWrite_P (PGM_P bytes, size_t len) override { return _currentClientSecure.write_P(bytes, len); } + +protected: + WiFiServerSecure _serverSecure; + WiFiClientSecure _currentClientSecure; +}; + + +#endif //ESP8266WEBSERVERSECURE_H diff --git a/libraries/ESP8266WiFi/examples/WiFiHTTPSServer/WiFiHTTPSServer.ino b/libraries/ESP8266WiFi/examples/WiFiHTTPSServer/WiFiHTTPSServer.ino new file mode 100644 index 0000000000..17a504cd9d --- /dev/null +++ b/libraries/ESP8266WiFi/examples/WiFiHTTPSServer/WiFiHTTPSServer.ino @@ -0,0 +1,204 @@ +/* + WiFiHTTPSServer - Simple SSL server example + + This sketch demonstrates how to set up a simple HTTP-like server usingcl + HTTPS encryption. This is NOT the recommended way of writing a web server! + Please see the ESP8266WebServer and ESP8266WebServerSecure for a much easier + and more robust server for your own applications. + + The server will set a GPIO pin depending on the request + https://server_ip/gpio/0 will set the GPIO2 low, + https://server_ip/gpio/1 will set the GPIO2 high + server_ip is the IP address of the ESP8266 module, will be + printed to Serial when the module is connected. + + IMPORTANT NOTES ABOUT SSL CERTIFICATES + + 1. USE/GENERATE YOUR OWN CERTIFICATES + While a sample, self-signed certificate is included in this example, + it is ABSOLUTELY VITAL that you use your own SSL certificate in any + real-world deployment. Anyone with the certificate and key may be + able to decrypt your traffic, so your own keys should be kept in a + safe manner, not accessible on any public network. + + 2. HOW TO GENERATE YOUR OWN CERTIFICATE/KEY PAIR + A sample script, "make-self-signed-cert.sh" is provided in the + ESP8266WiFi/examples/WiFiHTTPSServer directory. This script can be + modified (replace "your-name-here" with your Organization name). Note + that this will be a *self-signed certificate* and will *NOT* be accepted + by default by most modern browsers. They'll display something like, + "This certificate is from an untrusted source," or "Your connection is + not secure," or "Your connection is not private," and the user will + have to manully allow the browser to continue by using the + "Advanced/Add Exception" (FireFox) or "Advanced/Proceed" (Chrome) link. + + You may also, of course, use a commercial, trusted SSL provider to + generate your certificate. When requesting the certificate, you'll + need to specify that it use SHA256 and 1024 or 512 bits in order to + function with the axTLS implementation in the ESP8266. + + Adapted by Earle F. Philhower, III, from the WiFiWebServer.ino example. + This example is released into the public domain. +*/ + +#include + +const char* ssid = "your-ssid"; +const char* password = "your-password"; + +// The certificate is stored in PMEM +static const uint8_t x509[] PROGMEM = { + 0x30, 0x82, 0x01, 0x3d, 0x30, 0x81, 0xe8, 0x02, 0x09, 0x00, 0xfe, 0x56, + 0x46, 0xf2, 0x78, 0xc6, 0x51, 0x17, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x26, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x45, 0x53, + 0x50, 0x38, 0x32, 0x36, 0x36, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, + 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x31, 0x38, 0x31, + 0x34, 0x34, 0x39, 0x31, 0x38, 0x5a, 0x17, 0x0d, 0x33, 0x30, 0x31, 0x31, + 0x32, 0x35, 0x31, 0x34, 0x34, 0x39, 0x31, 0x38, 0x5a, 0x30, 0x26, 0x31, + 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x07, 0x45, 0x53, + 0x50, 0x38, 0x32, 0x36, 0x36, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x30, 0x2e, + 0x31, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, + 0x41, 0x00, 0xc6, 0x72, 0x6c, 0x12, 0xe1, 0x20, 0x4d, 0x10, 0x0c, 0xf7, + 0x3a, 0x2a, 0x5a, 0x49, 0xe2, 0x2d, 0xc9, 0x7a, 0x63, 0x1d, 0xef, 0xc6, + 0xbb, 0xa3, 0xd6, 0x6f, 0x59, 0xcb, 0xd5, 0xf6, 0xbe, 0x34, 0x83, 0x33, + 0x50, 0x80, 0xec, 0x49, 0x63, 0xbf, 0xee, 0x59, 0x94, 0x67, 0x8b, 0x8d, + 0x81, 0x85, 0x23, 0x24, 0x06, 0x52, 0x76, 0x55, 0x9d, 0x18, 0x09, 0xb3, + 0x3c, 0x10, 0x40, 0x05, 0x01, 0xf3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x41, 0x00, 0x69, 0xdc, 0x6c, 0x9b, 0xa7, 0x62, 0x57, + 0x7e, 0x03, 0x01, 0x45, 0xad, 0x9a, 0x83, 0x90, 0x3a, 0xe7, 0xdf, 0xe8, + 0x8f, 0x46, 0x00, 0xd3, 0x5f, 0x2b, 0x0a, 0xde, 0x92, 0x1b, 0xc5, 0x04, + 0xc5, 0xc0, 0x76, 0xf4, 0xf6, 0x08, 0x36, 0x97, 0x27, 0x82, 0xf1, 0x60, + 0x76, 0xc2, 0xcd, 0x67, 0x6c, 0x4b, 0x6c, 0xca, 0xfd, 0x97, 0xfd, 0x33, + 0x9e, 0x12, 0x67, 0x6b, 0x98, 0x7e, 0xd5, 0x80, 0x8f +}; + +// And so is the key. These could also be in DRAM +static const uint8_t rsakey[] PROGMEM = { + 0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00, 0xc6, 0x72, + 0x6c, 0x12, 0xe1, 0x20, 0x4d, 0x10, 0x0c, 0xf7, 0x3a, 0x2a, 0x5a, 0x49, + 0xe2, 0x2d, 0xc9, 0x7a, 0x63, 0x1d, 0xef, 0xc6, 0xbb, 0xa3, 0xd6, 0x6f, + 0x59, 0xcb, 0xd5, 0xf6, 0xbe, 0x34, 0x83, 0x33, 0x50, 0x80, 0xec, 0x49, + 0x63, 0xbf, 0xee, 0x59, 0x94, 0x67, 0x8b, 0x8d, 0x81, 0x85, 0x23, 0x24, + 0x06, 0x52, 0x76, 0x55, 0x9d, 0x18, 0x09, 0xb3, 0x3c, 0x10, 0x40, 0x05, + 0x01, 0xf3, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x35, 0x0b, 0x74, + 0xd3, 0xff, 0x15, 0x51, 0x44, 0x0f, 0x13, 0x2e, 0x9b, 0x0f, 0x93, 0x5c, + 0x3f, 0xfc, 0xf1, 0x17, 0xf9, 0x72, 0x94, 0x5e, 0xa7, 0xc6, 0xb3, 0xf0, + 0xfe, 0xc9, 0x6c, 0xb1, 0x1e, 0x83, 0xb3, 0xc6, 0x45, 0x3a, 0x25, 0x60, + 0x7c, 0x3d, 0x92, 0x7d, 0x53, 0xec, 0x49, 0x8d, 0xb5, 0x45, 0x10, 0x99, + 0x9b, 0xc6, 0x22, 0x3a, 0x68, 0xc7, 0x13, 0x4e, 0xb6, 0x04, 0x61, 0x21, + 0x01, 0x02, 0x21, 0x00, 0xea, 0x8c, 0x21, 0xd4, 0x7f, 0x3f, 0xb6, 0x91, + 0xfa, 0xf8, 0xb9, 0x2d, 0xcb, 0x36, 0x36, 0x02, 0x5f, 0xf0, 0x0c, 0x6e, + 0x87, 0xaa, 0x5c, 0x14, 0xf6, 0x56, 0x8e, 0x12, 0x92, 0x25, 0xde, 0xb3, + 0x02, 0x21, 0x00, 0xd8, 0x99, 0x01, 0xf1, 0x04, 0x0b, 0x98, 0xa3, 0x71, + 0x56, 0x1d, 0xea, 0x6f, 0x45, 0xd1, 0x36, 0x70, 0x76, 0x8b, 0xab, 0x69, + 0x30, 0x58, 0x9c, 0xe0, 0x45, 0x97, 0xe7, 0xb6, 0xb5, 0xef, 0xc1, 0x02, + 0x21, 0x00, 0xa2, 0x01, 0x06, 0xc0, 0xf2, 0xdf, 0xbc, 0x28, 0x1a, 0xb4, + 0xbf, 0x9b, 0x5c, 0xd8, 0x65, 0xf7, 0xbf, 0xf2, 0x5b, 0x73, 0xe0, 0xeb, + 0x0f, 0xcd, 0x3e, 0xd5, 0x4c, 0x2e, 0x91, 0x99, 0xec, 0xb7, 0x02, 0x20, + 0x4b, 0x9d, 0x46, 0xd7, 0x3c, 0x01, 0x4c, 0x5d, 0x2a, 0xb0, 0xd4, 0xaa, + 0xc6, 0x03, 0xca, 0xa0, 0xc5, 0xac, 0x2c, 0xe0, 0x3f, 0x4d, 0x98, 0x71, + 0xd3, 0xbd, 0x97, 0xe5, 0x55, 0x9c, 0xb8, 0x41, 0x02, 0x20, 0x02, 0x42, + 0x9f, 0xd1, 0x06, 0x35, 0x3b, 0x42, 0xf5, 0x64, 0xaf, 0x6d, 0xbf, 0xcd, + 0x2c, 0x3a, 0xcd, 0x0a, 0x9a, 0x4d, 0x7c, 0xad, 0x29, 0xd6, 0x36, 0x57, + 0xd5, 0xdf, 0x34, 0xeb, 0x26, 0x03 +}; + +// Create an instance of the server +// specify the port to listen on as an argument +WiFiServerSecure server(443); + +void setup() { + Serial.begin(115200); + delay(10); + + // prepare GPIO2 + pinMode(2, OUTPUT); + digitalWrite(2, 0); + + // Connect to WiFi network + Serial.println(); + Serial.println(); + Serial.print("Connecting to "); + Serial.println(ssid); + + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.println("WiFi connected"); + + // Set the certificates from PMEM (if using DRAM remove the _P from the call) + server.setServerKeyAndCert_P(rsakey, sizeof(rsakey), x509, sizeof(x509)); + + // Start the server + server.begin(); + Serial.println("Server started"); + + // Print the IP address + Serial.println(WiFi.localIP()); +} + +void loop() { + // Check if a client has connected + WiFiClientSecure client = server.available(); + if (!client) { + return; + } + + // Wait until the client sends some data + Serial.println("new client"); + unsigned long timeout = millis() + 3000; + while(!client.available() && millis() < timeout){ + delay(1); + } + if (millis() > timeout) { + Serial.println("timeout"); + client.flush(); + client.stop(); + return; + } + + // Read the first line of the request + String req = client.readStringUntil('\r'); + Serial.println(req); + client.flush(); + + // Match the request + int val; + if (req.indexOf("/gpio/0") != -1) + val = 0; + else if (req.indexOf("/gpio/1") != -1) + val = 1; + else { + Serial.println("invalid request"); + client.print("HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n\r\n\r\nNot found"); + return; + } + + // Set GPIO2 according to the request + digitalWrite(2, val); + + client.flush(); + + // Prepare the response + String s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n\r\n\r\nGPIO is now "; + s += (val)?"high":"low"; + s += "\n"; + + // Send the response to the client + client.print(s); + delay(1); + Serial.println("Client disconnected"); + + // The client will actually be disconnected + // when the function returns and 'client' object is detroyed +} + diff --git a/libraries/ESP8266WiFi/examples/WiFiHTTPSServer/make-self-signed-cert.sh b/libraries/ESP8266WiFi/examples/WiFiHTTPSServer/make-self-signed-cert.sh new file mode 100755 index 0000000000..0744804e3e --- /dev/null +++ b/libraries/ESP8266WiFi/examples/WiFiHTTPSServer/make-self-signed-cert.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# This script generates a self-signed certificate for use by the ESP8266 +# Replace your-name-here with somethine appropriate before running and use +# the generated .H files in your code as follows: +# +# static const uint8_t rsakey[] ICACHE_RODATA_ATTR = { +# #include "key.h" +# }; +# +# static const uint8_t x509[] ICACHE_RODATA_ATTR = { +# #include "x509.h" +# }; +# +# .... +# WiFiServerSecure server(443); +# server.setServerKeyAndCert_P(rsakey, sizeof(rsakey), x509, sizeof(x509)); +# .... + +# 1024 or 512. 512 saves memory... +BITS=512 +C=$PWD +pushd /tmp + +openssl genrsa -out tls.ca_key.pem $BITS +openssl genrsa -out tls.key_$BITS.pem $BITS +openssl rsa -in tls.key_$BITS.pem -out tls.key_$BITS -outform DER +cat > certs.conf < "$C/key.h" +xxd -i tls.x509_$BITS.cer | sed 's/.*{//' | sed 's/\};//' | sed 's/unsigned.*//' > "$C/x509.h" + +rm -f tls.ca_key.pem tls.key_$BITS.pem tls.key_$BITS certs.conf tls.ca_x509.req tls.x509_$BITS.req tls.ca_x509.pem tls.x509_$BITS.pem tls.srl tls.x509_$BITS.cer tls.ca_x509.cer + +popd diff --git a/libraries/ESP8266WiFi/keywords.txt b/libraries/ESP8266WiFi/keywords.txt index 7e7819f11c..f782e3d021 100644 --- a/libraries/ESP8266WiFi/keywords.txt +++ b/libraries/ESP8266WiFi/keywords.txt @@ -15,6 +15,7 @@ ESP8266WiFi KEYWORD3 WiFi KEYWORD1 WiFiClient KEYWORD1 WiFiServer KEYWORD1 +WiFiServerSecure KEYWORD1 WiFiUDP KEYWORD1 WiFiClientSecure KEYWORD1 ESP8266WiFiMulti KEYWORD1 diff --git a/libraries/ESP8266WiFi/src/ESP8266WiFi.h b/libraries/ESP8266WiFi/src/ESP8266WiFi.h index 50223af19e..9d6e7f9942 100644 --- a/libraries/ESP8266WiFi/src/ESP8266WiFi.h +++ b/libraries/ESP8266WiFi/src/ESP8266WiFi.h @@ -38,6 +38,7 @@ extern "C" { #include "WiFiClient.h" #include "WiFiServer.h" +#include "WiFiServerSecure.h" #include "WiFiClientSecure.h" #ifdef DEBUG_ESP_WIFI diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 5dcc43186f..ac37b181ee 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -53,7 +53,7 @@ class WiFiClient : public Client, public SList { virtual int connect(const String host, uint16_t port); virtual size_t write(uint8_t); virtual size_t write(const uint8_t *buf, size_t size); - size_t write_P(PGM_P buf, size_t size); + virtual size_t write_P(PGM_P buf, size_t size); size_t write(Stream& stream); // This one is deprecated, use write(Stream& instead) diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp index b82d0a6eab..53c86c3422 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp @@ -93,8 +93,6 @@ class SSLContext if (_ssl_ctx_refcnt == 0) { ssl_ctx_free(_ssl_ctx); } - - s_io_ctx = nullptr; } void ref() @@ -116,14 +114,14 @@ class SSLContext if (_ssl) { /* Creating a new TLS session on top of a new TCP connection. ssl_free will want to send a close notify alert, but the old TCP connection - is already gone at this point, so reset s_io_ctx. */ - s_io_ctx = nullptr; + is already gone at this point, so reset io_ctx. */ + io_ctx = nullptr; ssl_free(_ssl); _available = 0; _read_ptr = nullptr; } - s_io_ctx = ctx; - _ssl = ssl_client_new(_ssl_ctx, 0, nullptr, 0, ext); + io_ctx = ctx; + _ssl = ssl_client_new(_ssl_ctx, reinterpret_cast(this), nullptr, 0, ext); uint32_t t = millis(); while (millis() - t < timeout_ms && ssl_handshake_status(_ssl) != SSL_OK) { @@ -136,14 +134,32 @@ class SSLContext } } + void connectServer(ClientContext *ctx) { + io_ctx = ctx; + _ssl = ssl_server_new(_ssl_ctx, reinterpret_cast(this)); + _isServer = true; + + uint32_t timeout_ms = 5000; + uint32_t t = millis(); + + while (millis() - t < timeout_ms && ssl_handshake_status(_ssl) != SSL_OK) { + uint8_t* data; + int rc = ssl_read(_ssl, &data); + if (rc < SSL_OK) { + break; + } + } + } + void stop() { - s_io_ctx = nullptr; + io_ctx = nullptr; } bool connected() { - return _ssl != nullptr && ssl_handshake_status(_ssl) == SSL_OK; + if (_isServer) return _ssl != nullptr; + else return _ssl != nullptr && ssl_handshake_status(_ssl) == SSL_OK; } int read(uint8_t* dst, size_t size) @@ -189,7 +205,9 @@ class SSLContext int write(const uint8_t* src, size_t size) { - if (!_available) { + if (_isServer) { + return _write(src, size); + } else if (!_available) { if (_hasWriteBuffers()) { int rc = _writeBuffersSend(); if (rc < 0) { @@ -241,10 +259,10 @@ class SSLContext return cb; } - // similar to availble, but doesn't return exact size + // similar to available, but doesn't return exact size bool hasData() { - return _available > 0 || (s_io_ctx && s_io_ctx->getSize() > 0); + return _available > 0 || (io_ctx && io_ctx->getSize() > 0); } bool loadObject(int type, Stream& stream, size_t size) @@ -308,8 +326,15 @@ class SSLContext static ClientContext* getIOContext(int fd) { - (void) fd; - return s_io_ctx; + return reinterpret_cast(fd)->io_ctx; + } + + int loadServerX509Cert(const uint8_t *cert, int len) { + return ssl_obj_memory_load(SSLContext::_ssl_ctx, SSL_OBJ_X509_CERT, cert, len, NULL); + } + + int loadServerRSAKey(const uint8_t *rsakey, int len) { + return ssl_obj_memory_load(SSLContext::_ssl_ctx, SSL_OBJ_RSA_KEY, rsakey, len, NULL); } protected: @@ -386,6 +411,7 @@ class SSLContext return !_writeBuffers.empty(); } + bool _isServer = false; static SSL_CTX* _ssl_ctx; static int _ssl_ctx_refcnt; SSL* _ssl = nullptr; @@ -394,12 +420,11 @@ class SSLContext size_t _available = 0; BufferList _writeBuffers; bool _allowSelfSignedCerts = false; - static ClientContext* s_io_ctx; + ClientContext* io_ctx; }; SSL_CTX* SSLContext::_ssl_ctx = nullptr; int SSLContext::_ssl_ctx_refcnt = 0; -ClientContext* SSLContext::s_io_ctx = nullptr; WiFiClientSecure::WiFiClientSecure() { @@ -433,6 +458,42 @@ WiFiClientSecure& WiFiClientSecure::operator=(const WiFiClientSecure& rhs) return *this; } +// Only called by the WifiServerSecure, need to get the keys/certs loaded before beginning +WiFiClientSecure::WiFiClientSecure(ClientContext* client, bool usePMEM, const uint8_t *rsakey, int rsakeyLen, const uint8_t *cert, int certLen) +{ + _client = client; + if (_ssl) { + _ssl->unref(); + _ssl = nullptr; + } + + _ssl = new SSLContext; + _ssl->ref(); + + if (usePMEM) { + // When using PMEM based certs, allocate stack and copy from flash to DRAM, call SSL functions to avoid + // heap fragmentation that would happen w/malloc() + uint8_t *stackData = (uint8_t*)alloca(max(certLen, rsakeyLen)); + if (rsakey && rsakeyLen) { + memcpy_P(stackData, rsakey, rsakeyLen); + _ssl->loadServerRSAKey(stackData, rsakeyLen); + } + if (cert && certLen) { + memcpy_P(stackData, cert, certLen); + _ssl->loadServerX509Cert(stackData, certLen); + } + } else { + if (rsakey && rsakeyLen) { + _ssl->loadServerRSAKey(rsakey, rsakeyLen); + } + if (cert && certLen) { + _ssl->loadServerX509Cert(cert, certLen); + } + } + _client->ref(); + _ssl->connectServer(client); +} + int WiFiClientSecure::connect(IPAddress ip, uint16_t port) { if (!WiFiClient::connect(ip, port)) { @@ -496,6 +557,14 @@ size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) return 0; } +size_t WiFiClientSecure::write_P(PGM_P buf, size_t size) +{ + // Copy to RAM and call normal send. alloca() auto-frees on return + uint8_t *copy = (uint8_t*)alloca(size); + memcpy_P(copy, buf, size); + return write(copy, size); +} + int WiFiClientSecure::read(uint8_t *buf, size_t size) { if (!_ssl) { @@ -584,6 +653,8 @@ void WiFiClientSecure::stop() { if (_ssl) { _ssl->stop(); + _ssl->unref(); + _ssl = nullptr; } WiFiClient::stop(); } diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.h b/libraries/ESP8266WiFi/src/WiFiClientSecure.h index 94d0e429fb..9b7cf8df10 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.h @@ -44,6 +44,7 @@ class WiFiClientSecure : public WiFiClient { uint8_t connected() override; size_t write(const uint8_t *buf, size_t size) override; + size_t write_P(PGM_P buf, size_t size) override; int read(uint8_t *buf, size_t size) override; int available() override; int read() override; @@ -80,6 +81,10 @@ class WiFiClientSecure : public WiFiClient { return loadCACert(file, file.size()); } +friend class WiFiServerSecure; // Needs access to custom constructor below +protected: + // Only called by WiFiServerSecure + WiFiClientSecure(ClientContext* client, bool usePMEM, const uint8_t *rsakey, int rsakeyLen, const uint8_t *cert, int certLen); protected: void _initSSLContext(); diff --git a/libraries/ESP8266WiFi/src/WiFiServer.h b/libraries/ESP8266WiFi/src/WiFiServer.h index 8d16e9faa3..a327925fa8 100644 --- a/libraries/ESP8266WiFi/src/WiFiServer.h +++ b/libraries/ESP8266WiFi/src/WiFiServer.h @@ -35,7 +35,8 @@ class ClientContext; class WiFiClient; class WiFiServer : public Server { -private: + // Secure server needs access to all the private entries here +protected: uint16_t _port; IPAddress _addr; tcp_pcb* _pcb; diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecure.cpp b/libraries/ESP8266WiFi/src/WiFiServerSecure.cpp new file mode 100644 index 0000000000..ec7c52fa10 --- /dev/null +++ b/libraries/ESP8266WiFi/src/WiFiServerSecure.cpp @@ -0,0 +1,79 @@ +/* + WiFiServerSecure.cpp - SSL server for esp8266, mostly compatible + with Arduino WiFi shield library + + Copyright (c) 2017 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#define LWIP_INTERNAL + +extern "C" { + #include "osapi.h" + #include "ets_sys.h" +} + +#include "debug.h" +#include "ESP8266WiFi.h" +#include "WiFiClient.h" +#include "WiFiServer.h" +#include "lwip/opt.h" +#include "lwip/tcp.h" +#include "lwip/inet.h" +#include "include/ClientContext.h" +#include "WiFiServerSecure.h" + +WiFiServerSecure::WiFiServerSecure(IPAddress addr, uint16_t port) : WiFiServer(addr, port) +{ +} + +WiFiServerSecure::WiFiServerSecure(uint16_t port) : WiFiServer(port) +{ +} + +void WiFiServerSecure::setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) +{ + this->usePMEM = false; + this->rsakey = key; + this->rsakeyLen = keyLen; + this->cert = cert; + this->certLen = certLen; +} + +void WiFiServerSecure::setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen) +{ + this->usePMEM = true; + this->rsakey = key; + this->rsakeyLen = keyLen; + this->cert = cert; + this->certLen = certLen; +} + +WiFiClientSecure WiFiServerSecure::available(uint8_t* status) +{ + (void) status; // Unused + if (_unclaimed) { + WiFiClientSecure result(_unclaimed, usePMEM, rsakey, rsakeyLen, cert, certLen); + _unclaimed = _unclaimed->next(); + result.setNoDelay(_noDelay); + DEBUGV("WS:av\r\n"); + return result; + } + + optimistic_yield(1000); + return WiFiClientSecure(); +} + diff --git a/libraries/ESP8266WiFi/src/WiFiServerSecure.h b/libraries/ESP8266WiFi/src/WiFiServerSecure.h new file mode 100644 index 0000000000..2f78970a0a --- /dev/null +++ b/libraries/ESP8266WiFi/src/WiFiServerSecure.h @@ -0,0 +1,43 @@ +/* + WiFiServerSecure.h - Library for Arduino ESP8266 + Copyright (c) 2017 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef wifiserversecure_h +#define wifiserversecure_h + +#include "WiFiServer.h" +class WiFiClientSecure; + +class WiFiServerSecure : public WiFiServer { +public: + WiFiServerSecure(IPAddress addr, uint16_t port); + WiFiServerSecure(uint16_t port); + void setServerKeyAndCert(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + void setServerKeyAndCert_P(const uint8_t *key, int keyLen, const uint8_t *cert, int certLen); + virtual ~WiFiServerSecure() {} + WiFiClientSecure available(uint8_t* status = NULL); +private: + bool usePMEM = false; + const uint8_t *rsakey = nullptr; + int rsakeyLen = 0; + const uint8_t *cert = nullptr; + int certLen = 0; +}; + +#endif + From 1ee78c6d1437ba281d830b811dc2aa4ba4e2ae5c Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Fri, 9 Feb 2018 08:38:51 -0800 Subject: [PATCH 2/2] Wrap mimetype strings in FSPTR()s Mimetype is now in progmem, so any accesses to it need to be using FPSTR() wrapped Strings. Fixes #4329 --- libraries/ESP8266WebServer/src/ESP8266WebServer.cpp | 10 +++++----- .../ESP8266WebServer/src/detail/RequestHandlersImpl.h | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 4eef8725a6..ff7c58dfab 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -195,7 +195,7 @@ bool ESP8266WebServer::authenticate(const char * username, const char * password #endif md5.begin(); if(authReq.indexOf(FPSTR(qop_auth)) != -1) { - md5.add(_H1 + FPSTR(colon) + _nonce + FPSTR(colon) + _nc + FPSTR(colon) + _cnonce + ':auth:' + _H2); + md5.add(_H1 + FPSTR(colon) + _nonce + FPSTR(colon) + _nc + FPSTR(colon) + _cnonce + ":auth:" + _H2); }else{ md5.add(_H1 + FPSTR(colon) + _nonce + FPSTR(colon) + _H2); } @@ -376,7 +376,7 @@ void ESP8266WebServer::_prepareHeader(String& response, int code, const char* co if (!content_type) content_type = mimeTable[html].mimeType; - sendHeader(String(F("Content-Type")), content_type, true); + sendHeader(String(F("Content-Type")), String(FPSTR(content_type)), true); if (_contentLength == CONTENT_LENGTH_NOT_SET) { sendHeader(String(FPSTR(Content_Length)), String(contentLength)); } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) { @@ -485,9 +485,9 @@ void ESP8266WebServer::_streamFileCore(const size_t fileSize, const String & fil { using namespace mime; setContentLength(fileSize); - if (fileName.endsWith(mimeTable[gz].endsWith) && - contentType != mimeTable[gz].mimeType && - contentType != mimeTable[none].mimeType) { + if (fileName.endsWith(String(FPSTR(mimeTable[gz].endsWith))) && + contentType != String(FPSTR(mimeTable[gz].mimeType)) && + contentType != String(FPSTR(mimeTable[none].mimeType))) { sendHeader(F("Content-Encoding"), F("gzip")); } send(200, contentType, ""); diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h index b1de4a6005..63f0339995 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandlersImpl.h @@ -3,6 +3,7 @@ #include "RequestHandler.h" #include "mimetable.h" +#include "WString.h" using namespace mime; @@ -101,10 +102,10 @@ class StaticRequestHandler : public RequestHandler { // look for gz file, only if the original specified path is not a gz. So part only works to send gzip via content encoding when a non compressed is asked for // if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc... - if (!path.endsWith(mimeTable[gz].endsWith) && !_fs.exists(path)) { - String pathWithGz = path + mimeTable[gz].endsWith; + if (!path.endsWith(FPSTR(mimeTable[gz].endsWith)) && !_fs.exists(path)) { + String pathWithGz = path + FPSTR(mimeTable[gz].endsWith); if(_fs.exists(pathWithGz)) - path += mimeTable[gz].endsWith; + path += FPSTR(mimeTable[gz].endsWith); } File f = _fs.open(path, "r");