From 8ab3231e31cb3b94d80cbfd7dd1c1eac6f646236 Mon Sep 17 00:00:00 2001 From: copercini Date: Fri, 10 Feb 2017 20:20:24 -0200 Subject: [PATCH] Add WiFiClient secure lib (#184) * Provide SSL/TLS functions to ESP32 with Arduino IDE * Generate a new random number in case of reconnection --- .../WiFiClientSecure/WiFiClientSecure.ino | 76 ++++ libraries/WiFiClientSecure/keywords.txt | 35 ++ libraries/WiFiClientSecure/library.properties | 9 + .../WiFiClientSecure/src/WiFiClientSecure.cpp | 190 ++++++++++ .../WiFiClientSecure/src/WiFiClientSecure.h | 92 +++++ libraries/WiFiClientSecure/src/ssl_client.cpp | 327 ++++++++++++++++++ libraries/WiFiClientSecure/src/ssl_client.h | 37 ++ 7 files changed, 766 insertions(+) create mode 100644 libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino create mode 100644 libraries/WiFiClientSecure/keywords.txt create mode 100644 libraries/WiFiClientSecure/library.properties create mode 100644 libraries/WiFiClientSecure/src/WiFiClientSecure.cpp create mode 100644 libraries/WiFiClientSecure/src/WiFiClientSecure.h create mode 100644 libraries/WiFiClientSecure/src/ssl_client.cpp create mode 100644 libraries/WiFiClientSecure/src/ssl_client.h diff --git a/libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino b/libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino new file mode 100644 index 00000000..72edf966 --- /dev/null +++ b/libraries/WiFiClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino @@ -0,0 +1,76 @@ +/* + Wifi secure connection example for ESP32 + Running on TLS 1.2 using mbedTLS + Suporting the following chipersuites: + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_CCM","TLS_DHE_RSA_WITH_AES_256_CCM","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384","TLS_DHE_RSA_WITH_AES_256_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA","TLS_DHE_RSA_WITH_AES_256_CBC_SHA","TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8","TLS_DHE_RSA_WITH_AES_256_CCM_8","TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384","TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384","TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384","TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384","TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384","TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256","TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA","TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256","TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CCM","TLS_DHE_RSA_WITH_AES_128_CCM","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256","TLS_DHE_RSA_WITH_AES_128_CBC_SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA","TLS_DHE_RSA_WITH_AES_128_CBC_SHA","TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8","TLS_DHE_RSA_WITH_AES_128_CCM_8","TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256","TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256","TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256","TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256","TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256","TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256","TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA","TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA","TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA","TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA","TLS_DHE_PSK_WITH_AES_256_GCM_SHA384","TLS_DHE_PSK_WITH_AES_256_CCM","TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384","TLS_DHE_PSK_WITH_AES_256_CBC_SHA384","TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA","TLS_DHE_PSK_WITH_AES_256_CBC_SHA","TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384","TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384","TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384","TLS_PSK_DHE_WITH_AES_256_CCM_8","TLS_DHE_PSK_WITH_AES_128_GCM_SHA256","TLS_DHE_PSK_WITH_AES_128_CCM","TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256","TLS_DHE_PSK_WITH_AES_128_CBC_SHA256","TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA","TLS_DHE_PSK_WITH_AES_128_CBC_SHA","TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256","TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256","TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256","TLS_PSK_DHE_WITH_AES_128_CCM_8","TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA","TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA","TLS_RSA_WITH_AES_256_GCM_SHA384","TLS_RSA_WITH_AES_256_CCM","TLS_RSA_WITH_AES_256_CBC_SHA256","TLS_RSA_WITH_AES_256_CBC_SHA","TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA","TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA","TLS_RSA_WITH_AES_256_CCM_8","TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384","TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256","TLS_RSA_WITH_CAMELLIA_256_CBC_SHA","TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384","TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384","TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384","TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384","TLS_RSA_WITH_AES_128_GCM_SHA256","TLS_RSA_WITH_AES_128_CCM","TLS_RSA_WITH_AES_128_CBC_SHA256","TLS_RSA_WITH_AES_128_CBC_SHA","TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA","TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA","TLS_RSA_WITH_AES_128_CCM_8","TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256","TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256","TLS_RSA_WITH_CAMELLIA_128_CBC_SHA","TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256","TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256","TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256","TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256","TLS_RSA_WITH_3DES_EDE_CBC_SHA","TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA","TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA","TLS_RSA_PSK_WITH_AES_256_GCM_SHA384","TLS_RSA_PSK_WITH_AES_256_CBC_SHA384","TLS_RSA_PSK_WITH_AES_256_CBC_SHA","TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384","TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384","TLS_RSA_PSK_WITH_AES_128_GCM_SHA256","TLS_RSA_PSK_WITH_AES_128_CBC_SHA256","TLS_RSA_PSK_WITH_AES_128_CBC_SHA","TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256","TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256","TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA","TLS_PSK_WITH_AES_256_GCM_SHA384","TLS_PSK_WITH_AES_256_CCM","TLS_PSK_WITH_AES_256_CBC_SHA384","TLS_PSK_WITH_AES_256_CBC_SHA","TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384","TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384","TLS_PSK_WITH_AES_256_CCM_8","TLS_PSK_WITH_AES_128_GCM_SHA256","TLS_PSK_WITH_AES_128_CCM","TLS_PSK_WITH_AES_128_CBC_SHA256","TLS_PSK_WITH_AES_128_CBC_SHA","TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256","TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256","TLS_PSK_WITH_AES_128_CCM_8","TLS_PSK_WITH_3DES_EDE_CBC_SHA","TLS_EMPTY_RENEGOTIATION_INFO_SCSV"] + 2017 - Evandro Copercini - Apache 2.0 License. +*/ + +#include + +char ssid[] = "your_network_name"; // your network SSID (name of wifi network) +char pass[] = "your_password"; // your network password + +char server[] = "www.howsmyssl.com"; // Server URL +// You can use x.509 certificates if you want +//unsigned char test_ca_cert[] = ""; //For the usage of verifying server +//unsigned char test_client_key[] = ""; //For the usage of verifying client +//unsigned char test_client_cert[] = ""; //For the usage of verifying client + + +WiFiClientSecure client; + +void setup() { + //Initialize serial and wait for port to open: + Serial.begin(115200); + delay(100); + + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + WiFi.begin(ssid, pass); + + // attempt to connect to Wifi network: + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + // wait 1 second for re-trying + delay(1000); + } + + Serial.print("Connected to "); + Serial.println(ssid); + + Serial.println("\nStarting connection to server..."); + if (client.connect(server, 443)) { //client.connect(server, 443, test_ca_cert, test_client_cert, test_client_key) + Serial.println("Connected to server!"); + // Make a HTTP request: + client.println("GET https://www.howsmyssl.com/a/check HTTP/1.0"); + client.println("Host: www.howsmyssl.com"); + client.println("Connection: close"); + client.println(); + } + else + Serial.println("Connection failed!"); + + Serial.print("Waiting for response "); //WiFiClientSecure uses a non blocking implementation + while (!client.available()){ + delay(50); // + Serial.print("."); + } + // if there are incoming bytes available + // from the server, read them and print them: + while (client.available()) { + char c = client.read(); + Serial.write(c); + } + + // if the server's disconnected, stop the client: + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting from server."); + client.stop(); + } +} + +void loop() { + // do nothing +} \ No newline at end of file diff --git a/libraries/WiFiClientSecure/keywords.txt b/libraries/WiFiClientSecure/keywords.txt new file mode 100644 index 00000000..b1bf2c73 --- /dev/null +++ b/libraries/WiFiClientSecure/keywords.txt @@ -0,0 +1,35 @@ +####################################### +# Syntax Coloring Map For WiFi +####################################### + +####################################### +# Library (KEYWORD3) +####################################### + +WiFiClientSecure KEYWORD3 + +####################################### +# Datatypes (KEYWORD1) +####################################### + +WiFiClientSecure KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +connect KEYWORD2 +write KEYWORD2 +available KEYWORD2 +config KEYWORD2 +read KEYWORD2 +flush KEYWORD2 +stop KEYWORD2 +connected KEYWORD2 +setCACert KEYWORD2 +setCertificate KEYWORD2 +setPrivateKey KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/WiFiClientSecure/library.properties b/libraries/WiFiClientSecure/library.properties new file mode 100644 index 00000000..7e932754 --- /dev/null +++ b/libraries/WiFiClientSecure/library.properties @@ -0,0 +1,9 @@ +name=WiFiClientSecure +version=1.0 +author=Evandro Luis Copercini +maintainer=Github Community +sentence=Enables secure network connection (local and Internet) using the ESP32 built-in WiFi. +paragraph=With this library you can make a TLS or SSL connection to a remote server. +category=Communication +url= +architectures=esp32 diff --git a/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp new file mode 100644 index 00000000..51d063b2 --- /dev/null +++ b/libraries/WiFiClientSecure/src/WiFiClientSecure.cpp @@ -0,0 +1,190 @@ +/* + WiFiClientSecure.cpp - Client Secure class for ESP32 + Copyright (c) 2016 Hristo Gochkov All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + + 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 +*/ + +#include "WiFiClientSecure.h" +#include +#include +#include + +#undef connect +#undef write +#undef read + + +WiFiClientSecure::WiFiClientSecure() +{ + _connected = false; + + sslclient = new sslclient_context; + ssl_init(sslclient); + sslclient->socket = -1; + + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; +} + + +WiFiClientSecure::WiFiClientSecure(int sock) +{ + _connected = false; + + sslclient = new sslclient_context; + ssl_init(sslclient); + sslclient->socket = sock; + + if (sock >= 0) { + _connected = true; + } + + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; +} + +WiFiClientSecure::~WiFiClientSecure() +{ + stop(); +} + +WiFiClientSecure &WiFiClientSecure::operator=(const WiFiClientSecure &other) +{ + stop(); + sslclient->socket = other.sslclient->socket; + _connected = other._connected; + return *this; +} + +void WiFiClientSecure::stop() +{ + if (_connected && sslclient->socket >= 0) { + stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key); + sslclient->socket = -1; + _connected = false; + } +} + +int WiFiClientSecure::connect(IPAddress ip, uint16_t port) +{ + connect(ip, port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecure::connect(const char *host, uint16_t port) +{ + connect(host, port, _CA_cert, _cert, _private_key); +} + +int WiFiClientSecure::connect(IPAddress ip, uint16_t port, unsigned char *_CA_cert, unsigned char *_cert, unsigned char *_private_key) +{ + int ret = start_ssl_client(sslclient, ip, port, _CA_cert, _cert, _private_key); + if (ret < 0) { + log_e("lwip_connect_r: %d", errno); + } + _connected = true; + return 1; +} + +int WiFiClientSecure::connect(const char *host, uint16_t port, unsigned char *_CA_cert, unsigned char *_cert, unsigned char *_private_key) +{ + struct hostent *server; + server = gethostbyname(host); + if (server == NULL) { + return 0; + } + IPAddress srv((const uint8_t *)(server->h_addr)); + return connect(srv, port, _CA_cert, _cert, _private_key); +} + + +size_t WiFiClientSecure::write(uint8_t data) +{ + return write(&data, 1); +} + +int WiFiClientSecure::read() +{ + uint8_t data = 0; + int res = read(&data, 1); + if (res < 0) { + return res; + } + return data; +} + +size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) +{ + if (!_connected) { + return 0; + } + int res = send_ssl_data(sslclient, buf, size); + if (res < 0) { + log_e("%d", errno); + stop(); + res = 0; + } + return res; +} + +int WiFiClientSecure::read(uint8_t *buf, size_t size) +{ + if (!available()) { + return -1; + } + int res = get_ssl_receive(sslclient, buf, size); + if (res < 0 && errno != EWOULDBLOCK) { + printf("%d", errno); + stop(); + } + return res; +} + +int WiFiClientSecure::available() +{ + if (!_connected) { + return 0; + } + int res = data_to_read(sslclient); + + return res; +} + +uint8_t WiFiClientSecure::connected() +{ + uint8_t dummy = 0; + read(&dummy, 0); + + return _connected; +} + +void WiFiClientSecure::setCACert(unsigned char *rootCA) +{ + _CA_cert = rootCA; +} + +void WiFiClientSecure::setCertificate (unsigned char *client_ca) +{ + _cert = client_ca; +} + +void WiFiClientSecure::setPrivateKey (unsigned char *private_key) +{ + _private_key = private_key; +} + diff --git a/libraries/WiFiClientSecure/src/WiFiClientSecure.h b/libraries/WiFiClientSecure/src/WiFiClientSecure.h new file mode 100644 index 00000000..a3d7fc6c --- /dev/null +++ b/libraries/WiFiClientSecure/src/WiFiClientSecure.h @@ -0,0 +1,92 @@ +/* + WiFiClientSecure.h - Base class that provides Client SSL to ESP32 + Copyright (c) 2011 Adrian McEwen. All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + + 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 WiFiClientSecure_h +#define WiFiClientSecure_h +#include "Arduino.h" +#include "IPAddress.h" +#include +#include "ssl_client.h" + +class WiFiClientSecure : public Client +{ +protected: + bool _connected; + sslclient_context *sslclient; + + unsigned char *_CA_cert; + unsigned char *_cert; + unsigned char *_private_key; + +public: + WiFiClientSecure *next; + WiFiClientSecure(); + WiFiClientSecure(int socket); + ~WiFiClientSecure(); + int connect(IPAddress ip, uint16_t port); + int connect(const char *host, uint16_t port); + int connect(IPAddress ip, uint16_t port, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key); + int connect(const char *host, uint16_t port, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key); + size_t write(uint8_t data); + size_t write(const uint8_t *buf, size_t size); + int available(); + int read(); + int read(uint8_t *buf, size_t size); + int peek() + { + return 0; + } + void flush() {} + void stop(); + uint8_t connected(); + + void setCACert(unsigned char *rootCA); + void setCertificate(unsigned char *client_ca); + void setPrivateKey (unsigned char *private_key); + + operator bool() + { + return connected(); + } + WiFiClientSecure &operator=(const WiFiClientSecure &other); + bool operator==(const bool value) + { + return bool() == value; + } + bool operator!=(const bool value) + { + return bool() != value; + } + bool operator==(const WiFiClientSecure &); + bool operator!=(const WiFiClientSecure &rhs) + { + return !this->operator==(rhs); + }; + + int socket() + { + return sslclient->socket = -1; + } + + //friend class WiFiServer; + using Print::write; +}; + +#endif /* _WIFICLIENT_H_ */ diff --git a/libraries/WiFiClientSecure/src/ssl_client.cpp b/libraries/WiFiClientSecure/src/ssl_client.cpp new file mode 100644 index 00000000..eb348ff1 --- /dev/null +++ b/libraries/WiFiClientSecure/src/ssl_client.cpp @@ -0,0 +1,327 @@ +/* Provide SSL/TLS functions to ESP32 with Arduino IDE +* +* Adapted from the ssl_client1 example in mbedtls. +* +* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License. +* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. +*/ + +#include "Arduino.h" +#include +#include +#include +#include +#include + +#include "ssl_client.h" + + +const char *pers = "esp32-tls"; + +#define DEBUG true //Set false to supress debug messages + +#ifdef DEBUG +#define DEBUG_PRINT(...) printf( __VA_ARGS__ ) +#else +#define DEBUG_PRINT(x) +#endif + +#ifdef MBEDTLS_DEBUG_C + +#define MBEDTLS_DEBUG_LEVEL 4 + +/* mbedtls debug function that translates mbedTLS debug output +to ESP_LOGx debug output. + +MBEDTLS_DEBUG_LEVEL 4 means all mbedTLS debug output gets sent here, +and then filtered to the ESP logging mechanism. +*/ +static void mbedtls_debug(void *ctx, int level, + const char *file, int line, + const char *str) +{ + const char *MBTAG = "mbedtls"; + char *file_sep; + + /* Shorten 'file' from the whole file path to just the filename + + This is a bit wasteful because the macros are compiled in with + the full _FILE_ path in each case. + */ + file_sep = rindex(file, '/'); + if (file_sep) { + file = file_sep + 1; + } + + switch (level) { + case 1: + printf( "%s:%d %s \n", file, line, str); + break; + case 2: + case 3: + printf( "%s:%d %s \n", file, line, str); + case 4: + printf( "%s:%d %s \n", file, line, str); + break; + default: + printf( "Unexpected log level %d: %s \n", level, str); + break; + } +} + +#endif + + + + +void ssl_init(sslclient_context *ssl_client) +{ + /* + * Initialize the RNG and the session data + */ + + mbedtls_ssl_init(&ssl_client->ssl_ctx); + mbedtls_ssl_config_init(&ssl_client->ssl_conf); + + mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx); +} + + + +int start_ssl_client(sslclient_context *ssl_client, uint32_t ipAddress, uint32_t port, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key) +{ + char buf[512]; + int ret, flags, len, timeout; + int enable = 1; + DEBUG_PRINT("Free heap before TLS %u\n", xPortGetFreeHeapSize()); + + + do { + ssl_client->socket = -1; + ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ssl_client->socket < 0) { + printf("\r\nERROR opening socket\r\n"); + return ssl_client->socket; + } + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = ipAddress; + serv_addr.sin_port = htons(port); + + if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { + timeout = 30000; + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)); + lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)); + } else { + printf("\r\nConnect to Server failed!\r\n"); + ret = -1; + break; + } + + fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); + + + DEBUG_PRINT( "Seeding the random number generator\n"); + mbedtls_entropy_init(&ssl_client->entropy_ctx); + + if ((ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func, + &ssl_client->entropy_ctx, (const unsigned char *) pers, strlen(pers))) != 0) { + printf( "mbedtls_ctr_drbg_seed returned %d \n", ret); + break; + } + + + /* MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and + MBEDTLS_SSL_VERIFY_NONE if not. + */ + if (rootCABuff != NULL) { + DEBUG_PRINT( "Loading CA cert\n"); + mbedtls_x509_crt_init(&ssl_client->ca_cert); + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen((const char *)rootCABuff) + 1); + mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); + //mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL ); + if (ret < 0) { + printf( "CA cert: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + break; + } + } else { + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); + } + + if (cli_cert != NULL && cli_key != NULL) { + mbedtls_x509_crt_init(&ssl_client->client_cert); + mbedtls_pk_init(&ssl_client->client_key); + + DEBUG_PRINT( "Loading CRT cert\n"); + + ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen((const char *)cli_cert) + 1); + + if (ret < 0) { + printf( "CRT cert: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + break; + } + + DEBUG_PRINT( "Loading private key\n"); + ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen((const char *)cli_key) + 1, NULL, 0); + + if (ret < 0) { + printf( "PRIVATE KEY: mbedtls_x509_crt_parse returned -0x%x\n\n", -ret); + break; + } + + mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key); + } + + /* + // TODO: implement match CN verification + + DEBUG_PRINT( "Setting hostname for TLS session...\n"); + + // Hostname set here should match CN in server certificate + if((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0) + { + printf( "mbedtls_ssl_set_hostname returned -0x%x\n", -ret); + break; + } + */ + + DEBUG_PRINT( "Setting up the SSL/TLS structure...\n"); + + if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + printf( "mbedtls_ssl_config_defaults returned %d\n", ret); + break; + } + + mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx); +#ifdef MBEDTLS_DEBUG_C + mbedtls_debug_set_threshold(MBEDTLS_DEBUG_LEVEL); + mbedtls_ssl_conf_dbg(&ssl_client->ssl_conf, mbedtls_debug, NULL); +#endif + + if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) { + printf( "mbedtls_ssl_setup returned -0x%x\n\n", -ret); + break; + } + + mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL ); + + DEBUG_PRINT( "Performing the SSL/TLS handshake...\n"); + + while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != -76) { + printf( "mbedtls_ssl_handshake returned -0x%x\n", -ret); + break; + } + delay(10); + vPortYield(); + } + + + if (cli_cert != NULL && cli_key != NULL) { + DEBUG_PRINT("Protocol is %s \nCiphersuite is %s\n", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx)); + if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) { + DEBUG_PRINT("Record expansion is %d\n", ret); + } else { + DEBUG_PRINT("Record expansion is unknown (compression)\n"); + } + } + + + DEBUG_PRINT( "Verifying peer X.509 certificate...\n"); + + if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) { + printf( "Failed to verify peer certificate!\n"); + bzero(buf, sizeof(buf)); + mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags); + printf( "verification info: %s\n", buf); + stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue. + } else { + DEBUG_PRINT( "Certificate verified.\n"); + } + + } while (0); + + DEBUG_PRINT("Free heap after TLS %u\n", xPortGetFreeHeapSize()); + + return ssl_client->socket; +} + + +void stop_ssl_socket(sslclient_context *ssl_client, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key) +{ + DEBUG_PRINT( "\nCleaning SSL connection.\n"); + close(ssl_client->socket); + ssl_client->socket = -1; + mbedtls_ssl_free(&ssl_client->ssl_ctx); + mbedtls_ssl_config_free(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); + mbedtls_entropy_free(&ssl_client->entropy_ctx); + + if (rootCABuff != NULL) { + mbedtls_x509_crt_free(&ssl_client->ca_cert); + } + + if (cli_cert != NULL) { + mbedtls_x509_crt_free(&ssl_client->client_cert); + } + + if (cli_key != NULL) { + mbedtls_pk_free(&ssl_client->client_key); + } +} + + +int data_to_read(sslclient_context *ssl_client) +{ + + int ret, res; + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0); + //printf("RET: %i\n",ret); //for low level debug + res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx); + //printf("RES: %i\n",res); + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0 && ret != -76) { + printf("MbedTLS error %i", ret); + } + + return res; +} + + + +int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len) +{ + //DEBUG_PRINT( "Writing HTTP request...\n"); //for low level debug + int ret = -1; + + while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != -76) { + printf( "mbedtls_ssl_write returned -0x%x\n", -ret); + break; + } + } + + len = ret; + //DEBUG_PRINT( "%d bytes written\n", len); //for low level debug + return ret; +} + + + +int get_ssl_receive(sslclient_context *ssl_client, uint8_t *data, int length) +{ + //DEBUG_PRINT( "Reading HTTP response...\n"); //for low level debug + int ret = -1; + + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length); + + //DEBUG_PRINT( "%d bytes readed\n", ret); //for low level debug + return ret; +} diff --git a/libraries/WiFiClientSecure/src/ssl_client.h b/libraries/WiFiClientSecure/src/ssl_client.h new file mode 100644 index 00000000..a4300572 --- /dev/null +++ b/libraries/WiFiClientSecure/src/ssl_client.h @@ -0,0 +1,37 @@ +/* Provide SSL/TLS functions to ESP32 with Arduino IDE + * by Evandro Copercini - 2017 - Apache 2.0 License + */ + +#ifndef ARD_SSL_H +#define ARD_SSL_H +#include "mbedtls/platform.h" +#include "mbedtls/net.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" + +typedef struct sslclient_context { + int socket; + mbedtls_net_context net_ctx; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + + mbedtls_ctr_drbg_context drbg_ctx; + mbedtls_entropy_context entropy_ctx; + + mbedtls_x509_crt ca_cert; + mbedtls_x509_crt client_cert; + mbedtls_pk_context client_key; +} sslclient_context; + + +void ssl_init(sslclient_context *ssl_client); +int start_ssl_client(sslclient_context *ssl_client, uint32_t ipAddress, uint32_t port, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key); +void stop_ssl_socket(sslclient_context *ssl_client, unsigned char *rootCABuff, unsigned char *cli_cert, unsigned char *cli_key); +int data_to_read(sslclient_context *ssl_client); +int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, uint16_t len); +int get_ssl_receive(sslclient_context *ssl_client, uint8_t *data, int length); + +#endif