1
0
mirror of https://github.com/esp8266/Arduino.git synced 2025-06-15 00:02:49 +03:00

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.
This commit is contained in:
Earle F. Philhower, III
2017-02-27 20:17:04 -08:00
committed by Ivan Grokhotkov
parent 8765da258b
commit bd1c7ce1dc
17 changed files with 1049 additions and 29 deletions

View File

@ -38,6 +38,7 @@ extern "C" {
#include "WiFiClient.h"
#include "WiFiServer.h"
#include "WiFiServerSecure.h"
#include "WiFiClientSecure.h"
#ifdef DEBUG_ESP_WIFI

View File

@ -53,7 +53,7 @@ public:
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)

View File

@ -93,8 +93,6 @@ public:
if (_ssl_ctx_refcnt == 0) {
ssl_ctx_free(_ssl_ctx);
}
s_io_ctx = nullptr;
}
void ref()
@ -116,14 +114,14 @@ public:
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<int>(this), nullptr, 0, ext);
uint32_t t = millis();
while (millis() - t < timeout_ms && ssl_handshake_status(_ssl) != SSL_OK) {
@ -136,14 +134,32 @@ public:
}
}
void connectServer(ClientContext *ctx) {
io_ctx = ctx;
_ssl = ssl_server_new(_ssl_ctx, reinterpret_cast<int>(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 @@ public:
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 @@ public:
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 @@ public:
static ClientContext* getIOContext(int fd)
{
(void) fd;
return s_io_ctx;
return reinterpret_cast<SSLContext*>(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 @@ protected:
return !_writeBuffers.empty();
}
bool _isServer = false;
static SSL_CTX* _ssl_ctx;
static int _ssl_ctx_refcnt;
SSL* _ssl = nullptr;
@ -394,12 +420,11 @@ protected:
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();
}

View File

@ -44,6 +44,7 @@ public:
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 @@ public:
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();

View File

@ -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;

View File

@ -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();
}

View File

@ -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