Skip to content

Commit

Permalink
Merge pull request #4 from mskvortsov/mbedtls
Browse files Browse the repository at this point in the history
Port to Mbed TLS
  • Loading branch information
thebentern authored Oct 28, 2024
2 parents 23665b3 + 2ab1741 commit 896f177
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 98 deletions.
2 changes: 1 addition & 1 deletion src/ConnectionContext.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <IPAddress.h>

// Required for SSL
#include "openssl/ssl.h"
#include "mbedtls/ssl.h"
#undef read

namespace httpsserver {
Expand Down
2 changes: 1 addition & 1 deletion src/HTTPResponse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#undef write
#include <vector>

#include <openssl/ssl.h>
#include <mbedtls/ssl.h>

#include "util.hpp"

Expand Down
106 changes: 76 additions & 30 deletions src/HTTPSConnection.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#include "HTTPSConnection.hpp"
#include "mbedtls/net_sockets.h"

namespace httpsserver {


HTTPSConnection::HTTPSConnection(ResourceResolver * resResolver):
HTTPConnection(resResolver) {
_ssl = NULL;
_sslCreated = false;
_socket = 0;
}

HTTPSConnection::~HTTPSConnection() {
Expand All @@ -17,38 +19,58 @@ bool HTTPSConnection::isSecure() {
return true;
}

bool HTTPSConnection::setup(mbedtls_ssl_config *sslConfig) {
mbedtls_ssl_init(&_ssl);
int res = mbedtls_ssl_setup(&_ssl, sslConfig);
if (res == 0) {
return true;
} else {
mbedtls_ssl_free(&_ssl);
return false;
}
}

bool HTTPSConnection::handshake() {
int res;
while (true) {
res = mbedtls_ssl_handshake(&_ssl);
if (res == 0) {
return true;
}
if (res != MBEDTLS_ERR_SSL_WANT_READ && res != MBEDTLS_ERR_SSL_WANT_WRITE) {
return false;
}
}
}

/**
* Initializes the connection from a server socket.
*
* 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, mbedtls_ssl_config *sslConfig, HTTPHeaders *defaultHeaders) {
if (_connectionState == STATE_UNDEFINED) {
// Let the base class connect the plain tcp socket
int resSocket = HTTPConnection::initialize(serverSocketID, defaultHeaders);

// Build up SSL Connection context if the socket has been created successfully
if (resSocket >= 0) {

_ssl = SSL_new(sslCtx);
_socket = resSocket;
_sslCreated = setup(sslConfig);

if (_ssl) {
if (_sslCreated) {
// 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);
}
mbedtls_ssl_set_bio(&_ssl, &_socket, mbedtls_net_send, mbedtls_net_recv, NULL);

// Perform the handshake
if (handshake()) {
return resSocket;
} else {
HTTPS_LOGE("SSL_set_fd failed. Aborting handshake. FID=%d", resSocket);
HTTPS_LOGE("SSL handshake failed. Aborting handshake. FID=%d", resSocket);
}
} else {
HTTPS_LOGE("SSL_new failed. Aborting handshake. FID=%d", resSocket);
HTTPS_LOGE("SSL setup failed. Aborting handshake. FID=%d", resSocket);
}

} else {
Expand All @@ -66,6 +88,18 @@ int HTTPSConnection::initialize(int serverSocketID, SSL_CTX * sslCtx, HTTPHeader
return -1;
}

int HTTPSConnection::shutdown() {
int res;
while (true) {
res = mbedtls_ssl_close_notify(&_ssl);
if (res == 0) {
return 1;
}
if (res != MBEDTLS_ERR_SSL_WANT_WRITE) {
return 0;
}
}
}

void HTTPSConnection::closeConnection() {

Expand All @@ -83,41 +117,53 @@ void HTTPSConnection::closeConnection() {
}

// 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
if (_sslCreated) {
if (_connectionState == STATE_ERROR || shutdown() == 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;
mbedtls_ssl_free(&_ssl);
_sslCreated = false;
} 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");
// The timeout has been hit, we force SSL shutdown now by resetting the session
mbedtls_ssl_free(&_ssl);
_sslCreated = false;
HTTPS_LOGW("SSL shutdown did not receive close notification from the client");
_connectionState = STATE_ERROR;
}
}

// If SSL has been brought down, close the socket
if (!_ssl) {
if (!_sslCreated) {
HTTPConnection::closeConnection();
}
}

size_t HTTPSConnection::writeBuffer(byte* buffer, size_t length) {
return SSL_write(_ssl, buffer, length);
while (true) {
int res = mbedtls_ssl_write(&_ssl, buffer, length);
if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) {
continue;
}
return res;
}
}

size_t HTTPSConnection::readBytesToBuffer(byte* buffer, size_t length) {
return SSL_read(_ssl, buffer, length);
while (true) {
int res = mbedtls_ssl_read(&_ssl, buffer, length);
if (res == MBEDTLS_ERR_SSL_WANT_READ || res == MBEDTLS_ERR_SSL_WANT_WRITE) {
continue;
}
return res;
}
}

size_t HTTPSConnection::pendingByteCount() {
return SSL_pending(_ssl);
return mbedtls_ssl_get_bytes_avail(&_ssl);
}

bool HTTPSConnection::canReadData() {
return HTTPConnection::canReadData() || (SSL_pending(_ssl) > 0);
return HTTPConnection::canReadData() || (mbedtls_ssl_get_bytes_avail(&_ssl) > 0);
}

} /* namespace httpsserver */
11 changes: 8 additions & 3 deletions src/HTTPSConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <string>

// Required for SSL
#include "openssl/ssl.h"
#include "mbedtls/ssl.h"
#undef read

// Required for sockets
Expand Down Expand Up @@ -34,9 +34,12 @@ class HTTPSConnection : public HTTPConnection {
HTTPSConnection(ResourceResolver * resResolver);
virtual ~HTTPSConnection();

virtual int initialize(int serverSocketID, SSL_CTX * sslCtx, HTTPHeaders *defaultHeaders);
virtual int initialize(int serverSocketID, mbedtls_ssl_config *sslConfig, HTTPHeaders *defaultHeaders);
virtual void closeConnection();
virtual bool isSecure();
bool setup(mbedtls_ssl_config *sslConfig);
bool handshake();
int shutdown();

protected:
friend class HTTPRequest;
Expand All @@ -49,7 +52,9 @@ class HTTPSConnection : public HTTPConnection {

private:
// SSL context for this connection
SSL * _ssl;
mbedtls_ssl_context _ssl;
bool _sslCreated;
int _socket;

};

Expand Down
110 changes: 51 additions & 59 deletions src/HTTPSServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,95 +8,87 @@ HTTPSServer::HTTPSServer(SSLCert * cert, const uint16_t port, const uint8_t maxC
_cert(cert) {

// Configure runtime data
_sslctx = NULL;
mbedtls_ssl_config_init(&_ssl_config);
mbedtls_x509_crt_init(&_ssl_cert);
mbedtls_pk_init(&_ssl_pk);
mbedtls_entropy_init(&_ssl_entropy);
mbedtls_ctr_drbg_init(&_ssl_ctr_drbg);

mbedtls_ctr_drbg_seed(&_ssl_ctr_drbg, mbedtls_entropy_func, &_ssl_entropy, nullptr, 0);
mbedtls_ssl_conf_rng(&_ssl_config, mbedtls_ctr_drbg_random, &_ssl_ctr_drbg);
}

HTTPSServer::~HTTPSServer() {

mbedtls_ctr_drbg_init(&_ssl_ctr_drbg);
mbedtls_entropy_init(&_ssl_entropy);
mbedtls_pk_free(&_ssl_pk);
mbedtls_x509_crt_free(&_ssl_cert);
mbedtls_ssl_config_free(&_ssl_config);
}

/**
* This method starts the server and begins to listen on the port
*/
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;
}

if (HTTPServer::setupSocket()) {
return 1;
} else {
Serial.println("setupSockets failed");
SSL_CTX_free(_sslctx);
_sslctx = NULL;
return 0;
}
} else {
if (isRunning()) {
return 1;
}

int res = mbedtls_ssl_config_defaults(&_ssl_config,
MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
if (res != 0) {
Serial.println("Setting SSL default config failed");
return 0;
}

if (setupCert() != 0) {
Serial.println("setupCert failed");
return 0;
}

if (!HTTPServer::setupSocket()) {
Serial.println("setupSockets failed");
return 0;
}

return 1;
}

void HTTPSServer::teardownSocket() {

HTTPServer::teardownSocket();

// Tear down the SSL context
SSL_CTX_free(_sslctx);
_sslctx = 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, &_ssl_config, &_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()
);
int HTTPSServer::setupCert() {
int res = 0;

res = mbedtls_x509_crt_parse(&_ssl_cert, _cert->getCertData(), _cert->getCertLength());
if (res != 0) {
return res;
}

#if ESP_IDF_VERSION_MAJOR > 4
res = mbedtls_pk_parse_key(&_ssl_pk, _cert->getPKData(), _cert->getPKLength(), nullptr, 0,
mbedtls_ctr_drbg_random, &_ssl_ctr_drbg);
#else
res = mbedtls_pk_parse_key(&_ssl_pk, _cert->getPKData(), _cert->getPKLength(), nullptr, 0);
#endif
if (res != 0) {
return res;
}

return ret;
return mbedtls_ssl_conf_own_cert(&_ssl_config, &_ssl_cert, &_ssl_pk);
}

} /* namespace httpsserver */
Loading

0 comments on commit 896f177

Please sign in to comment.