diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ba97b..4b48fbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,13 @@ # Changelog -## [pending (master)](https://github.com/fhessel/esp32_https_server/tree/master) +## [v1.1.1](https://github.com/jackjansen/esp32_idf5_https_server/tree/master) -New functionality: - -– - -Bug fixes: - -– +- Addressed issues with esp32-arduino-core v3 and esp-idf 5.0 -Breaking changes: +## [v1.1.0](https://github.com/jackjansen/esp32_idf5_https_server/tree/master) -– +- Compatible with ESP-IDF 5.0 +- Use esp-tls in stead of openssl ## [v1.0.0](https://github.com/fhessel/esp32_https_server/releases/tag/v1.0.0) diff --git a/example.conf b/example.conf new file mode 100644 index 0000000..01f46b1 --- /dev/null +++ b/example.conf @@ -0,0 +1,9 @@ +[ req ] +distinguished_name = req_distinguished_name +prompt = no +[ req_distinguished_name ] +C = DE +ST = BE +L = Berlin +O = MyCompany +CN = esp32.local diff --git a/example.crt b/example.crt new file mode 100644 index 0000000..a27ff33 --- /dev/null +++ b/example.crt @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICYTCCAcqgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJERTEL +MAkGA1UECAwCQkUxDzANBgNVBAcMBkJlcmxpbjESMBAGA1UECgwJTXlDb21wYW55 +MRMwEQYDVQQDDApteWNhLmxvY2FsMB4XDTI0MDcwOTEwMTEwN1oXDTM0MDcwNzEw +MTEwN1owVTELMAkGA1UEBhMCREUxCzAJBgNVBAgMAkJFMQ8wDQYDVQQHDAZCZXJs +aW4xEjAQBgNVBAoMCU15Q29tcGFueTEUMBIGA1UEAwwLZXNwMzIubG9jYWwwgZ8w +DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK/aY9jhRfEAgfu3KeTNQ1T2CshDP12e +benmLPnnVG830HQhMLIe1itN/f0tPTguUYtOBgUS9w2zeZEdm6aqeskkkV55Wnmz +IhGNg/cuwl/zp98NbOsZ4Rwcjp5dtL5w8UnlIsK8PsFntRWHQOthgaKH3YzyGg65 +ZtPKD13/ZGXbAgMBAAGjQjBAMB0GA1UdDgQWBBTBnWcv/0066SQSwr7nvZc0bg8B +fzAfBgNVHSMEGDAWgBTCqpgyA21IXR4Maq4makkkGgy66TANBgkqhkiG9w0BAQsF +AAOBgQAV0W4470+7/P5bRd94GYyF16/DR1W/H4NITptAJsMGjdyOaRVQnebCYP73 +idqMu9LUk0XGqG7StMys5n0VatMjhqOKrZF67wYEgGlaPYTRfpHEDMCPDLB/myYC +OpkxBG45CHzNz2tABRxsfPXN4+8bZpz0y6LUoeQD/ZMcMmZ2sA== +-----END CERTIFICATE----- diff --git a/library.json b/library.json index 0456c47..0542003 100644 --- a/library.json +++ b/library.json @@ -5,10 +5,10 @@ "repository": { "type": "git", - "url": "https://github.com/fhessel/esp32_https_server.git" + "url": "https://github.com/bligeti/esp32_https_server.git" }, "license": "MIT", - "version": "1.0.0", + "version": "1.1.0", "frameworks": "arduino", "platforms": ["espressif32"] } diff --git a/library.properties b/library.properties index 3ffb202..5ccdfb9 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ name=ESP32_HTTPS_Server -version=1.0.0 -author=Frank Hessel -maintainer=Frank Hessel +version=1.1.0 +author=bligeti +maintainer=bligeti sentence=Alternative ESP32 Webserver implementation for the ESP32, supporting HTTPS and HTTP. paragraph=The library provides TLS support and simultaneous connections. It can be used to run an HTTP or HTTPS server, or both in parallel. The server's resources are defined through handler and middleware functions, giving an easy start to everyone who has worked with frameworks like Express or Servlets before. category=Communication -url=https://github.com/fhessel/esp32_https_server +url=https://github.com/blgeti/esp32_https_server architectures=esp32 includes=HTTPSServer.hpp,HTTPRequest.hpp,HTTPResponse.hpp diff --git a/src/ConnectionContext.hpp b/src/ConnectionContext.hpp index da88964..e6a2a46 100644 --- a/src/ConnectionContext.hpp +++ b/src/ConnectionContext.hpp @@ -5,9 +5,7 @@ #include // Required for SSL -#include "openssl/ssl.h" -#undef read - +#include namespace httpsserver { class WebsocketHandler; diff --git a/src/HTTPConnection.cpp b/src/HTTPConnection.cpp index 0ab739c..809852c 100644 --- a/src/HTTPConnection.cpp +++ b/src/HTTPConnection.cpp @@ -1,5 +1,6 @@ #include "HTTPConnection.hpp" - +#include "mbedtls/base64.h" +#include "mbedtls/sha1.h" namespace httpsserver { HTTPConnection::HTTPConnection(ResourceResolver * resResolver): @@ -138,7 +139,8 @@ void HTTPConnection::closeConnection() { } if (_wsHandler != nullptr) { - HTTPS_LOGD("Free WS Handler"); + HTTPS_LOGD("Free WS Handler in closeConnection()"); + _wsHandler->onClose(); delete _wsHandler; _wsHandler = NULL; } @@ -206,7 +208,7 @@ int HTTPConnection::updateBuffer() { } else { // An error occured _connectionState = STATE_ERROR; - HTTPS_LOGE("An receive error occured, FID=%d", _socket); + HTTPS_LOGE("An receive error occured, FID=%d, SSL_error=%d", _socket, readReturnCode); closeConnection(); return -1; } @@ -238,6 +240,7 @@ bool HTTPConnection::canReadData() { size_t HTTPConnection::readBuffer(byte* buffer, size_t length) { updateBuffer(); + if (isClosed()) return 0; size_t bufferSize = _bufferUnusedIdx - _bufferProcessed; if (length > bufferSize) { @@ -254,7 +257,7 @@ size_t HTTPConnection::readBuffer(byte* buffer, size_t length) { size_t HTTPConnection::pendingBufferSize() { updateBuffer(); - + if (isClosed()) return 0; return _bufferUnusedIdx - _bufferProcessed + pendingByteCount(); } @@ -303,6 +306,10 @@ void HTTPConnection::readLine(int lengthLimit) { raiseError(400, "Bad Request"); return; } + } else if (_bufferProcessed+1 == _bufferUnusedIdx){ + //Look ahead was not possible so let it go for the next round + _parserLine.possiblenewlineoverflow = true; + return; } } else { _parserLine.text += newChar; @@ -389,7 +396,7 @@ void HTTPConnection::loop() { HTTPS_LOGI("Request: %s %s (FID=%d)", _httpMethod.c_str(), _httpResource.c_str(), _socket); _connectionState = STATE_REQUEST_FINISHED; } - + if (_parserLine.possiblenewlineoverflow) _parserLine.possiblenewlineoverflow = false; break; case STATE_REQUEST_FINISHED: // Read headers @@ -422,6 +429,10 @@ void HTTPConnection::loop() { _parserLine.parsingFinished = false; _parserLine.text = ""; + } + if (_parserLine.possiblenewlineoverflow) { + _parserLine.possiblenewlineoverflow = false; + break; } } @@ -482,7 +493,8 @@ void HTTPConnection::loop() { // Find the request handler callback HTTPSCallbackFunction * resourceCallback; - if (websocketRequested) { + if (websocketRequested && resolvedResource.getMatchingNode()->_nodeType == WEBSOCKET) { + //if (websocketRequested) { // For the websocket, we use the handshake callback defined below resourceCallback = &handleWebsocketHandshake; } else { @@ -518,7 +530,7 @@ void HTTPConnection::loop() { } // Finally, after the handshake is done, we create the WebsocketHandler and change the internal state. - if(websocketRequested) { + if(websocketRequested && res.getStatusCode() == 101 &&resolvedResource.getMatchingNode()->_nodeType == WEBSOCKET) { _wsHandler = ((WebsocketNode*)resolvedResource.getMatchingNode())->newHandler(); _wsHandler->initialize(this); // make websocket with this connection _connectionState = STATE_WEBSOCKET; @@ -576,23 +588,28 @@ void HTTPConnection::loop() { break; case STATE_WEBSOCKET: // Do handling of the websocket refreshTimeout(); // don't timeout websocket connection - if(pendingBufferSize() > 0) { + //re-checking the _connectionState since it can change at pendingBufferSize() + if(pendingBufferSize() > 0 && !isClosed() ) { HTTPS_LOGD("Calling WS handler, FID=%d", _socket); _wsHandler->loop(); } // If the client closed the connection unexpectedly - if (_clientState == CSTATE_CLOSED) { + if (_clientState == CSTATE_CLOSED && !isClosed()) { HTTPS_LOGI("WS lost client, calling onClose, FID=%d", _socket); _wsHandler->onClose(); } // If the handler has terminated the connection, clean up and close the socket too - if (_wsHandler->closed() || _clientState == CSTATE_CLOSED) { - HTTPS_LOGI("WS closed, freeing Handler, FID=%d", _socket); - delete _wsHandler; - _wsHandler = nullptr; - _connectionState = STATE_CLOSING; + if (!isClosed()){ + if (_wsHandler->closed() || _clientState == CSTATE_CLOSED) { + HTTPS_LOGI("WS closed, freeing Handler, FID=%d", _socket); + delete _wsHandler; + _wsHandler = nullptr; + _connectionState = STATE_CLOSING; + } + } else { + HTTPS_LOGI("WS closed due to SSL level issue and cleanded up"); } break; default:; @@ -605,7 +622,7 @@ void HTTPConnection::loop() { bool HTTPConnection::checkWebsocket() { if(_httpMethod == "GET" && !_httpHeaders->getValue("Host").empty() && - _httpHeaders->getValue("Upgrade") == "websocket" && + (_httpHeaders->getValue("Upgrade") == "websocket" || _httpHeaders->getValue("Upgrade") == "WebSocket") && _httpHeaders->getValue("Connection").find("Upgrade") != std::string::npos && !_httpHeaders->getValue("Sec-WebSocket-Key").empty() && _httpHeaders->getValue("Sec-WebSocket-Version") == "13") { @@ -664,7 +681,7 @@ void handleWebsocketHandshake(HTTPRequest * req, HTTPResponse * res) { std::string websocketKeyResponseHash(std::string const &key) { std::string newKey = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; uint8_t shaData[HTTPS_SHA1_LENGTH]; - esp_sha(SHA1, (uint8_t*)newKey.data(), newKey.length(), shaData); + mbedtls_sha1((uint8_t*)newKey.data(), newKey.length(), shaData); // Get output size required for base64 representation size_t b64BufferSize = 0; diff --git a/src/HTTPConnection.hpp b/src/HTTPConnection.hpp index fb15d7a..2430ea4 100644 --- a/src/HTTPConnection.hpp +++ b/src/HTTPConnection.hpp @@ -6,7 +6,8 @@ #include #include -#include +#include +#include #include // Required for sockets @@ -107,6 +108,9 @@ class HTTPConnection : private ConnectionContext { CSTATE_ACTIVE, CSTATE_CLOSED } _clientState; + + //Websocket connection + WebsocketHandler * _wsHandler; private: void raiseError(uint16_t code, std::string reason); @@ -144,6 +148,7 @@ class HTTPConnection : private ConnectionContext { struct { std::string text = ""; bool parsingFinished = false; + bool possiblenewlineoverflow=false; } _parserLine; // HTTP properties: Method, Request, Headers @@ -157,8 +162,6 @@ class HTTPConnection : private ConnectionContext { // Should we use keep alive bool _isKeepAlive; - //Websocket connection - WebsocketHandler * _wsHandler; }; diff --git a/src/HTTPResponse.hpp b/src/HTTPResponse.hpp index 7bd1758..2ff5c88 100644 --- a/src/HTTPResponse.hpp +++ b/src/HTTPResponse.hpp @@ -9,7 +9,8 @@ #undef write #include -#include +//#include +#include #include "util.hpp" diff --git a/src/HTTPSConnection.cpp b/src/HTTPSConnection.cpp index e0e3dd0..c978d80 100644 --- a/src/HTTPSConnection.cpp +++ b/src/HTTPSConnection.cpp @@ -22,42 +22,33 @@ bool HTTPSConnection::isSecure() { * * The call WILL BLOCK if accept(serverSocketID) blocks. So use select() to check for that in advance. */ -int HTTPSConnection::initialize(int serverSocketID, SSL_CTX * sslCtx, HTTPHeaders *defaultHeaders) { +int HTTPSConnection::initialize(int serverSocketID, esp_tls_cfg_server_t * cfgSrv, HTTPHeaders *defaultHeaders) { if (_connectionState == STATE_UNDEFINED) { // Let the base class connect the plain tcp socket int resSocket = HTTPConnection::initialize(serverSocketID, defaultHeaders); - + HTTPS_LOGI("Cert len:%d, apn:%s",cfgSrv->servercert_bytes,cfgSrv->alpn_protos[0]); // Build up SSL Connection context if the socket has been created successfully if (resSocket >= 0) { + + _ssl = esp_tls_init(); - _ssl = SSL_new(sslCtx); - - if (_ssl) { - // Bind SSL to the socket - int success = SSL_set_fd(_ssl, resSocket); - if (success) { - - // Perform the handshake - success = SSL_accept(_ssl); - if (success) { - return resSocket; - } else { - HTTPS_LOGE("SSL_accept failed. Aborting handshake. FID=%d", resSocket); - } - } else { - HTTPS_LOGE("SSL_set_fd failed. Aborting handshake. FID=%d", resSocket); - } + if (!_ssl) { + HTTPS_LOGE("esp_tls_init failed. FID=%d", resSocket); + return -1; + } + + int res=esp_tls_server_session_create(cfgSrv,resSocket,_ssl); + if (res != 0) { + HTTPS_LOGE("esp_tls_server_session_create failed. FID=%d, Error=%d", resSocket, res); } else { - HTTPS_LOGE("SSL_new failed. Aborting handshake. FID=%d", resSocket); + return resSocket; } } else { HTTPS_LOGE("Could not accept() new connection. FID=%d", resSocket); } - _connectionState = STATE_ERROR; _clientState = CSTATE_ACTIVE; - // This will only be called if the connection could not be established and cleanup // variables like _ssl etc. closeConnection(); @@ -81,21 +72,15 @@ void HTTPSConnection::closeConnection() { // correctly _connectionState = STATE_CLOSING; } - + if (this->_wsHandler != nullptr) { + this->_wsHandler->onClose(); + } // Try to tear down SSL while we are in the _shutdownTS timeout period or if an error occurred if (_ssl) { - if(_connectionState == STATE_ERROR || SSL_shutdown(_ssl) == 0) { - // SSL_shutdown will return 1 as soon as the client answered with close notify - // This means we are safe to close the socket - SSL_free(_ssl); - _ssl = NULL; - } else if (_shutdownTS + HTTPS_SHUTDOWN_TIMEOUT < millis()) { - // The timeout has been hit, we force SSL shutdown now by freeing the context - SSL_free(_ssl); - _ssl = NULL; - HTTPS_LOGW("SSL_shutdown did not receive close notification from the client"); - _connectionState = STATE_ERROR; - } + + esp_tls_server_session_delete(_ssl); + _ssl = NULL; + _connectionState = STATE_ERROR; } // If SSL has been brought down, close the socket @@ -105,19 +90,24 @@ void HTTPSConnection::closeConnection() { } size_t HTTPSConnection::writeBuffer(byte* buffer, size_t length) { - return SSL_write(_ssl, buffer, length); + return esp_tls_conn_write(_ssl,buffer,length);// SSL_write(_ssl, buffer, length); } size_t HTTPSConnection::readBytesToBuffer(byte* buffer, size_t length) { - return SSL_read(_ssl, buffer, length); + int ret = esp_tls_conn_read(_ssl, buffer, length); + if (ret < 0) { + //HTTPS_LOGD("SSL_read error: %d", SSL_get_error(_ssl, ret)); + HTTPS_LOGD("SSL_read error"); + } + return ret; } size_t HTTPSConnection::pendingByteCount() { - return SSL_pending(_ssl); + return esp_tls_get_bytes_avail(_ssl); } bool HTTPSConnection::canReadData() { - return HTTPConnection::canReadData() || (SSL_pending(_ssl) > 0); + return HTTPConnection::canReadData() || (esp_tls_get_bytes_avail(_ssl) > 0); } } /* namespace httpsserver */ diff --git a/src/HTTPSConnection.hpp b/src/HTTPSConnection.hpp index 8adbce5..c4b1500 100644 --- a/src/HTTPSConnection.hpp +++ b/src/HTTPSConnection.hpp @@ -6,8 +6,9 @@ #include // Required for SSL -#include "openssl/ssl.h" -#undef read +//#include "openssl/ssl.h" +//#undef read +#include // Required for sockets #include "lwip/netdb.h" @@ -34,7 +35,7 @@ class HTTPSConnection : public HTTPConnection { HTTPSConnection(ResourceResolver * resResolver); virtual ~HTTPSConnection(); - virtual int initialize(int serverSocketID, SSL_CTX * sslCtx, HTTPHeaders *defaultHeaders); + virtual int initialize(int serverSocketID,esp_tls_cfg_server_t * cfgSrv, HTTPHeaders *defaultHeaders); virtual void closeConnection(); virtual bool isSecure(); @@ -49,8 +50,7 @@ class HTTPSConnection : public HTTPConnection { private: // SSL context for this connection - SSL * _ssl; - + esp_tls_t * _ssl; }; } /* namespace httpsserver */ diff --git a/src/HTTPSServer.cpp b/src/HTTPSServer.cpp index 4d8352d..098e669 100644 --- a/src/HTTPSServer.cpp +++ b/src/HTTPSServer.cpp @@ -2,17 +2,18 @@ namespace httpsserver { +constexpr const char * alpn_protos[] = { "http/1.1", NULL } ; HTTPSServer::HTTPSServer(SSLCert * cert, const uint16_t port, const uint8_t maxConnections, const in_addr_t bindAddress): HTTPServer(port, maxConnections, bindAddress), _cert(cert) { - // Configure runtime data - _sslctx = NULL; + _cfg = NULL; + } HTTPSServer::~HTTPSServer() { - + } /** @@ -20,24 +21,24 @@ HTTPSServer::~HTTPSServer() { */ uint8_t HTTPSServer::setupSocket() { if (!isRunning()) { - if (!setupSSLCTX()) { - Serial.println("setupSSLCTX failed"); - return 0; - } - - if (!setupCert()) { - Serial.println("setupCert failed"); - SSL_CTX_free(_sslctx); - _sslctx = NULL; - return 0; + _cfg = new esp_tls_cfg_server(); + _cfg->alpn_protos = (const char **)alpn_protos; + _cfg->cacert_buf = NULL; + _cfg->cacert_bytes = 0; + _cfg->servercert_buf = _cert->getCertData(); + _cfg->servercert_bytes = _cert->getCertLength(); + _cfg->serverkey_buf = _cert->getPKData(); + _cfg->serverkey_bytes = _cert->getPKLength(); + + esp_err_t ret = esp_tls_cfg_server_session_tickets_init(_cfg); + if ( ret != ESP_OK ) { + HTTPS_LOGE("Failed to init session ticket support. error: %s", esp_err_to_name(ret)); } if (HTTPServer::setupSocket()) { return 1; } else { - Serial.println("setupSockets failed"); - SSL_CTX_free(_sslctx); - _sslctx = NULL; + HTTPS_LOGE("HTTPServer::setupSocket failed"); return 0; } } else { @@ -49,54 +50,22 @@ void HTTPSServer::teardownSocket() { HTTPServer::teardownSocket(); - // Tear down the SSL context - SSL_CTX_free(_sslctx); - _sslctx = NULL; + if (_cfg) { + //esp_tls_cfg_server_session_tickets_free(_cfg); + free((void *)_cfg->servercert_buf); + free((void *)_cfg->serverkey_buf); + } + delete _cfg; + _cfg = NULL; + + } int HTTPSServer::createConnection(int idx) { HTTPSConnection * newConnection = new HTTPSConnection(this); _connections[idx] = newConnection; - return newConnection->initialize(_socket, _sslctx, &_defaultHeaders); -} - -/** - * This method configures the ssl context that is used for the server - */ -uint8_t HTTPSServer::setupSSLCTX() { - _sslctx = SSL_CTX_new(TLSv1_2_server_method()); - if (_sslctx) { - // Set SSL Timeout to 5 minutes - SSL_CTX_set_timeout(_sslctx, 300); - return 1; - } else { - _sslctx = NULL; - return 0; - } + return newConnection->initialize(_socket, _cfg , &_defaultHeaders); } -/** - * This method configures the certificate and private key for the given - * ssl context - */ -uint8_t HTTPSServer::setupCert() { - // Configure the certificate first - uint8_t ret = SSL_CTX_use_certificate_ASN1( - _sslctx, - _cert->getCertLength(), - _cert->getCertData() - ); - - // Then set the private key accordingly - if (ret) { - ret = SSL_CTX_use_RSAPrivateKey_ASN1( - _sslctx, - _cert->getPKData(), - _cert->getPKLength() - ); - } - - return ret; -} } /* namespace httpsserver */ diff --git a/src/HTTPSServer.hpp b/src/HTTPSServer.hpp index 68596bf..563e9ae 100644 --- a/src/HTTPSServer.hpp +++ b/src/HTTPSServer.hpp @@ -8,8 +8,9 @@ #include // Required for SSL -#include "openssl/ssl.h" -#undef read +//#include "openssl/ssl.h" +#include +//#undef read // Internal includes #include "HTTPServer.hpp" @@ -31,21 +32,19 @@ class HTTPSServer : public HTTPServer { public: HTTPSServer(SSLCert * cert, const uint16_t portHTTPS = 443, const uint8_t maxConnections = 4, const in_addr_t bindAddress = 0); virtual ~HTTPSServer(); - private: // Static configuration. Port, keys, etc. ==================== // Certificate that should be used (includes private key) SSLCert * _cert; //// Runtime data ============================================ - SSL_CTX * _sslctx; + esp_tls_cfg_server_t * _cfg; // Status of the server: Are we running, or not? // Setup functions virtual uint8_t setupSocket(); virtual void teardownSocket(); - uint8_t setupSSLCTX(); - uint8_t setupCert(); + // Helper functions virtual int createConnection(int idx); @@ -53,4 +52,4 @@ class HTTPSServer : public HTTPServer { } /* namespace httpsserver */ -#endif /* SRC_HTTPSSERVER_HPP_ */ +#endif /* SRC_HTTPSSERVER_HPP_ */ \ No newline at end of file diff --git a/src/SSLCert.cpp b/src/SSLCert.cpp index 3df7073..fe608fd 100644 --- a/src/SSLCert.cpp +++ b/src/SSLCert.cpp @@ -3,306 +3,321 @@ namespace httpsserver { SSLCert::SSLCert(unsigned char * certData, uint16_t certLength, unsigned char * pkData, uint16_t pkLength): - _certLength(certLength), - _certData(certData), - _pkLength(pkLength), - _pkData(pkData) { + _certLength(certLength), + _certData(certData), + _pkLength(pkLength), + _pkData(pkData) { } SSLCert::~SSLCert() { - // TODO Auto-generated destructor stub + // TODO Auto-generated destructor stub } uint16_t SSLCert::getCertLength() { - return _certLength; + return _certLength; } uint16_t SSLCert::getPKLength() { - return _pkLength; + return _pkLength; } unsigned char * SSLCert::getCertData() { - return _certData; + return _certData; } unsigned char * SSLCert::getPKData() { - return _pkData; + return _pkData; } void SSLCert::setPK(unsigned char * pkData, uint16_t length) { - _pkData = pkData; - _pkLength = length; + _pkData = pkData; + _pkLength = length; } void SSLCert::setCert(unsigned char * certData, uint16_t length) { - _certData = certData; - _certLength = length; + _certData = certData; + _certLength = length; } void SSLCert::clear() { - for(uint16_t i = 0; i < _certLength; i++) _certData[i]=0; - delete _certData; - _certLength = 0; + for(uint16_t i = 0; i < _certLength; i++) _certData[i]=0; + delete _certData; + _certLength = 0; - for(uint16_t i = 0; i < _pkLength; i++) _pkData[i] = 0; - delete _pkData; - _pkLength = 0; + for(uint16_t i = 0; i < _pkLength; i++) _pkData[i] = 0; + delete _pkData; + _pkLength = 0; } #ifndef HTTPS_DISABLE_SELFSIGNING /** * Function to create the key for a self-signed certificate. - * + * * Writes private key as DER in certCtx - * + * * Based on programs/pkey/gen_key.c */ static int gen_key(SSLCert &certCtx, SSLKeySize keySize) { - // Initialize the entropy source - mbedtls_entropy_context entropy; - mbedtls_entropy_init( &entropy ); - - // Initialize the RNG - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_ctr_drbg_init( &ctr_drbg ); - int rngRes = mbedtls_ctr_drbg_seed( - &ctr_drbg, mbedtls_entropy_func, &entropy, - NULL, 0 - ); - if (rngRes != 0) { - mbedtls_entropy_free( &entropy ); - return HTTPS_SERVER_ERROR_KEYGEN_RNG; - } - - // Initialize the private key - mbedtls_pk_context key; - mbedtls_pk_init( &key ); - int resPkSetup = mbedtls_pk_setup( &key, mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ); - if ( resPkSetup != 0) { - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); - return HTTPS_SERVER_ERROR_KEYGEN_SETUP_PK; - } - - // Actual key generation - int resPkGen = mbedtls_rsa_gen_key( - mbedtls_pk_rsa( key ), - mbedtls_ctr_drbg_random, - &ctr_drbg, - keySize, - 65537 - ); - if ( resPkGen != 0) { - mbedtls_pk_free( &key ); + // Initialize the entropy source + mbedtls_entropy_context entropy; + mbedtls_entropy_init( &entropy ); + + // Initialize the RNG + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ctr_drbg_init( &ctr_drbg ); + int rngRes = mbedtls_ctr_drbg_seed( + &ctr_drbg, mbedtls_entropy_func, &entropy, + NULL, 0 + ); + if (rngRes != 0) { + mbedtls_entropy_free( &entropy ); + return HTTPS_SERVER_ERROR_KEYGEN_RNG; + } + + // Initialize the private key + mbedtls_pk_context key; + mbedtls_pk_init( &key ); + int resPkSetup = mbedtls_pk_setup( &key, mbedtls_pk_info_from_type( MBEDTLS_PK_RSA ) ); + if ( resPkSetup != 0) { + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return HTTPS_SERVER_ERROR_KEYGEN_SETUP_PK; + } + + // Actual key generation + int resPkGen = mbedtls_rsa_gen_key( + mbedtls_pk_rsa( key ), + mbedtls_ctr_drbg_random, + &ctr_drbg, + keySize, + 65537 + ); + if ( resPkGen != 0) { + mbedtls_pk_free( &key ); + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return HTTPS_SERVER_ERROR_KEYGEN_GEN_PK; + } + + // Free the entropy source and the RNG as they are no longer needed mbedtls_ctr_drbg_free( &ctr_drbg ); mbedtls_entropy_free( &entropy ); - return HTTPS_SERVER_ERROR_KEYGEN_GEN_PK; - } - // Free the entropy source and the RNG as they are no longer needed - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); - - // Allocate the space on the heap, as stack size is quite limited - unsigned char * output_buf = new unsigned char[4096]; - if (output_buf == NULL) { - mbedtls_pk_free( &key ); - return HTTPS_SERVER_ERROR_KEY_OUT_OF_MEM; - } - memset(output_buf, 0, 4096); - - // Write the key to the temporary buffer and determine its length - int resPkWrite = mbedtls_pk_write_key_der( &key, output_buf, 4096 ); - if (resPkWrite < 0) { - delete[] output_buf; - mbedtls_pk_free( &key ); - return HTTPS_SERVER_ERROR_KEY_WRITE_PK; - } - size_t pkLength = resPkWrite; - unsigned char *pkOffset = output_buf + sizeof(unsigned char) * 4096 - pkLength; - - // Copy the key into a new, fitting space on the heap - unsigned char * output_pk = new unsigned char[pkLength]; - if (output_pk == NULL) { + // Allocate the space on the heap, as stack size is quite limited + unsigned char * output_buf = new unsigned char[4096]; + if (output_buf == NULL) { + mbedtls_pk_free( &key ); + return HTTPS_SERVER_ERROR_KEY_OUT_OF_MEM; + } + memset(output_buf, 0, 4096); + + // Write the key to the temporary buffer and determine its length + int resPkWrite = mbedtls_pk_write_key_der( &key, output_buf, 4096 ); + if (resPkWrite < 0) { + delete[] output_buf; + mbedtls_pk_free( &key ); + return HTTPS_SERVER_ERROR_KEY_WRITE_PK; + } + size_t pkLength = resPkWrite; + unsigned char *pkOffset = output_buf + sizeof(unsigned char) * 4096 - pkLength; + + // Copy the key into a new, fitting space on the heap + unsigned char * output_pk = new unsigned char[pkLength]; + if (output_pk == NULL) { + delete[] output_buf; + mbedtls_pk_free( &key ); + return HTTPS_SERVER_ERROR_KEY_WRITE_PK; + } + memcpy(output_pk, pkOffset, pkLength); + + // Clean up the temporary buffer and clear the key context delete[] output_buf; mbedtls_pk_free( &key ); - return HTTPS_SERVER_ERROR_KEY_WRITE_PK; - } - memcpy(output_pk, pkOffset, pkLength); - - // Clean up the temporary buffer and clear the key context - delete[] output_buf; - mbedtls_pk_free( &key ); - // Set the private key in the context - certCtx.setPK(output_pk, pkLength); + // Set the private key in the context + certCtx.setPK(output_pk, pkLength); - return 0; + return 0; } /** * Function to generate the X509 certificate data for a private key - * + * * Writes certificate in certCtx * * Based on programs/x509/cert_write.c */ static int cert_write(SSLCert &certCtx, std::string dn, std::string validityFrom, std::string validityTo) { - int funcRes = 0; - int stepRes = 0; - - mbedtls_entropy_context entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_pk_context key; - mbedtls_x509write_cert crt; - mbedtls_mpi serial; - unsigned char * primary_buffer; - unsigned char *certOffset; - unsigned char * output_buffer; - size_t certLength; - - // Make a C-friendly version of the distinguished name - char dn_cstr[dn.length()+1]; - strcpy(dn_cstr, dn.c_str()); - - // Initialize the entropy source - mbedtls_entropy_init( &entropy ); - - // Initialize the RNG - mbedtls_ctr_drbg_init( &ctr_drbg ); - stepRes = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 ); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_RNG; - goto error_after_entropy; - } - - mbedtls_pk_init( &key ); - stepRes = mbedtls_pk_parse_key( &key, certCtx.getPKData(), certCtx.getPKLength(), NULL, 0 ); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_READKEY; - goto error_after_key; - } - - // Start configuring the certificate - mbedtls_x509write_crt_init( &crt ); - // Set version and hash algorithm - mbedtls_x509write_crt_set_version( &crt, MBEDTLS_X509_CRT_VERSION_3 ); - mbedtls_x509write_crt_set_md_alg( &crt, MBEDTLS_MD_SHA256 ); - - // Set the keys (same key as we self-sign) - mbedtls_x509write_crt_set_subject_key( &crt, &key ); - mbedtls_x509write_crt_set_issuer_key( &crt, &key ); - - // Set issuer and subject (same, as we self-sign) - stepRes = mbedtls_x509write_crt_set_subject_name( &crt, dn_cstr ); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_NAME; - goto error_after_cert; - } - stepRes = mbedtls_x509write_crt_set_issuer_name( &crt, dn_cstr ); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_NAME; - goto error_after_cert; - } - - // Set the validity of the certificate. At the moment, it's fixed from 2019 to end of 2029. - stepRes = mbedtls_x509write_crt_set_validity( &crt, validityFrom.c_str(), validityTo.c_str()); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_VALIDITY; - goto error_after_cert; - } - - // Make this a CA certificate - stepRes = mbedtls_x509write_crt_set_basic_constraints( &crt, 1, 0 ); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_VALIDITY; - goto error_after_cert; - } - - // Initialize the serial number - mbedtls_mpi_init( &serial ); - stepRes = mbedtls_mpi_read_string( &serial, 10, "1" ); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_SERIAL; - goto error_after_cert_serial; - } - stepRes = mbedtls_x509write_crt_set_serial( &crt, &serial ); - if (stepRes != 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_SERIAL; - goto error_after_cert_serial; - } - - // Create buffer to write the certificate - primary_buffer = new unsigned char[4096]; - if (primary_buffer == NULL) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_OUT_OF_MEM; - goto error_after_cert_serial; - } - - // Write the actual certificate - stepRes = mbedtls_x509write_crt_der(&crt, primary_buffer, 4096, mbedtls_ctr_drbg_random, &ctr_drbg ); - if (stepRes < 0) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_WRITE; - goto error_after_primary_buffer; - } - - // Create a matching buffer - certLength = stepRes; - certOffset = primary_buffer + sizeof(unsigned char) * 4096 - certLength; - - // Copy the cert into a new, fitting space on the heap - output_buffer = new unsigned char[certLength]; - if (output_buffer == NULL) { - funcRes = HTTPS_SERVER_ERROR_CERTGEN_OUT_OF_MEM; - goto error_after_primary_buffer; - } - memcpy(output_buffer, certOffset, certLength); - - // Configure the cert in the context - certCtx.setCert(output_buffer, certLength); - - // Run through the cleanup process + int funcRes = 0; + int stepRes = 0; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_pk_context key; + mbedtls_x509write_cert crt; +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + const char *serial = "peer"; +#else + mbedtls_mpi serial; +#endif + + unsigned char * primary_buffer; + unsigned char *certOffset; + unsigned char * output_buffer; + size_t certLength; + + // Make a C-friendly version of the distinguished name + char dn_cstr[dn.length()+1]; + strcpy(dn_cstr, dn.c_str()); + + // Initialize the entropy source + mbedtls_entropy_init( &entropy ); + + // Initialize the RNG + mbedtls_ctr_drbg_init( &ctr_drbg ); + stepRes = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0 ); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_RNG; + goto error_after_entropy; + } +#if MBEDTLS_VERSION_NUMBER < 0x03040000 + mbedtls_pk_init( &key ); + stepRes = mbedtls_pk_parse_key( &key, certCtx.getPKData(), certCtx.getPKLength(), NULL, 0 ); +#else + stepRes = mbedtls_pk_parse_key( &key, + certCtx.getPKData(), + certCtx.getPKLength(), + NULL, 0, NULL, NULL); +#endif + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_READKEY; + goto error_after_key; + } + // Start configuring the certificate + mbedtls_x509write_crt_init( &crt ); + // Set version and hash algorithm + mbedtls_x509write_crt_set_version( &crt, MBEDTLS_X509_CRT_VERSION_3 ); + mbedtls_x509write_crt_set_md_alg( &crt, MBEDTLS_MD_SHA256 ); + + // Set the keys (same key as we self-sign) + mbedtls_x509write_crt_set_subject_key( &crt, &key ); + mbedtls_x509write_crt_set_issuer_key( &crt, &key ); + + // Set issuer and subject (same, as we self-sign) + stepRes = mbedtls_x509write_crt_set_subject_name( &crt, dn_cstr ); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_NAME; + goto error_after_cert; + } + stepRes = mbedtls_x509write_crt_set_issuer_name( &crt, dn_cstr ); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_NAME; + goto error_after_cert; + } + + // Set the validity of the certificate. At the moment, it's fixed from 2019 to end of 2029. + stepRes = mbedtls_x509write_crt_set_validity( &crt, validityFrom.c_str(), validityTo.c_str()); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_VALIDITY; + goto error_after_cert; + } + + // Make this a CA certificate + stepRes = mbedtls_x509write_crt_set_basic_constraints( &crt, 1, 0 ); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_VALIDITY; + goto error_after_cert; + } + +#if MBEDTLS_VERSION_NUMBER >= 0x03000000 + mbedtls_x509write_crt_set_serial_raw(&crt, (unsigned char *) serial, strlen(serial)); +#else + // generate random serial number + mbedtls_mpi_init( &serial ); + stepRes = mbedtls_mpi_fill_random( &serial, 10, mbedtls_ctr_drbg_random, &ctr_drbg ); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_SERIAL; + goto error_after_cert_serial; + } + stepRes = mbedtls_x509write_crt_set_serial( &crt, &serial ); + if (stepRes != 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_SERIAL; + goto error_after_cert_serial; + } +#endif + // Create buffer to write the certificate + primary_buffer = new unsigned char[4096]; + if (primary_buffer == NULL) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_OUT_OF_MEM; + goto error_after_cert_serial; + } + + // Write the actual certificate + stepRes = mbedtls_x509write_crt_der(&crt, primary_buffer, 4096, mbedtls_ctr_drbg_random, &ctr_drbg ); + if (stepRes < 0) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_WRITE; + goto error_after_primary_buffer; + } + + // Create a matching buffer + certLength = stepRes; + certOffset = primary_buffer + sizeof(unsigned char) * 4096 - certLength; + + // Copy the cert into a new, fitting space on the heap + output_buffer = new unsigned char[certLength]; + if (output_buffer == NULL) { + funcRes = HTTPS_SERVER_ERROR_CERTGEN_OUT_OF_MEM; + goto error_after_primary_buffer; + } + memcpy(output_buffer, certOffset, certLength); + + // Configure the cert in the context + certCtx.setCert(output_buffer, certLength); + + // Run through the cleanup process error_after_primary_buffer: - delete[] primary_buffer; + delete[] primary_buffer; error_after_cert_serial: - mbedtls_mpi_free( &serial ); +#if MBEDTLS_VERSION_NUMBER < 0x03000000 + mbedtls_mpi_free( &serial ); +#endif error_after_cert: - mbedtls_x509write_crt_free( &crt ); + mbedtls_x509write_crt_free( &crt ); error_after_key: - mbedtls_pk_free(&key); + mbedtls_pk_free(&key); error_after_entropy: - mbedtls_ctr_drbg_free( &ctr_drbg ); - mbedtls_entropy_free( &entropy ); - return funcRes; + mbedtls_ctr_drbg_free( &ctr_drbg ); + mbedtls_entropy_free( &entropy ); + return funcRes; } int createSelfSignedCert(SSLCert &certCtx, SSLKeySize keySize, std::string dn, std::string validFrom, std::string validUntil) { - // Add the private key - int keyRes = gen_key(certCtx, keySize); - if (keyRes != 0) { - // Key-generation failed, return the failure code - return keyRes; - } - - // Add the self-signed certificate - int certRes = cert_write(certCtx, dn, validFrom, validUntil); - if (certRes != 0) { - // Cert writing failed, reset the pk and return failure code - certCtx.setPK(NULL, 0); - return certRes; - } - - // If all went well, return 0 - return 0; + // Add the private key + int keyRes = gen_key(certCtx, keySize); + if (keyRes != 0) { + // Key-generation failed, return the failure code + return keyRes; + } + + // Add the self-signed certificate + int certRes = cert_write(certCtx, dn, validFrom, validUntil); + if (certRes != 0) { + // Cert writing failed, reset the pk and return failure code + certCtx.setPK(NULL, 0); + return certRes; + } + + // If all went well, return 0 + return 0; } #endif // !HTTPS_DISABLE_SELFSIGNING diff --git a/src/WebsocketHandler.cpp b/src/WebsocketHandler.cpp index 5e4c1d1..b444889 100644 --- a/src/WebsocketHandler.cpp +++ b/src/WebsocketHandler.cpp @@ -1,5 +1,9 @@ #include "WebsocketHandler.hpp" +#ifndef TAG +static const char *TAG = "WebsocketHandler"; +#endif + namespace httpsserver { /** @@ -113,7 +117,7 @@ int WebsocketHandler::read() { if (payloadLen == 0) { HTTPS_LOGW("WS payload not present"); } else { - HTTPS_LOGI("WS payload: length=%d", payloadLen); + HTTPS_LOGI("WS payload: length=%d", (int)payloadLen); } switch(frame.opCode) { @@ -250,5 +254,8 @@ void WebsocketHandler::send(uint8_t* data, uint16_t length, uint8_t sendType) { bool WebsocketHandler::closed() { return _receivedClose || _sentClose; } +bool WebsocketHandler::initialized() { + return (_con != nullptr); +} } diff --git a/src/WebsocketHandler.hpp b/src/WebsocketHandler.hpp index c8c710d..af902fd 100644 --- a/src/WebsocketHandler.hpp +++ b/src/WebsocketHandler.hpp @@ -70,6 +70,7 @@ class WebsocketHandler void send(std::string data, uint8_t sendType = SEND_TYPE_BINARY); void send(uint8_t *data, uint16_t length, uint8_t sendType = SEND_TYPE_BINARY); bool closed(); + bool initialized(); void loop(); void initialize(ConnectionContext * con);