From 7cb954abc8aa40139eb1c8bcae29237a83dee623 Mon Sep 17 00:00:00 2001
From: Liu Han <liuhan@espressif.com>
Date: Wed, 23 Jan 2019 16:23:45 +0800
Subject: [PATCH] feat: Add wolfSSL library in esp_tls component

---
 components/esp-tls/component.mk |  13 ++-
 components/esp-tls/esp_tls.c    | 171 ++++++++++++++++++++++++++++++--
 components/esp-tls/esp_tls.h    |  12 ++-
 3 files changed, 184 insertions(+), 12 deletions(-)

diff --git a/components/esp-tls/component.mk b/components/esp-tls/component.mk
index 680168b7..52aa5d7e 100644
--- a/components/esp-tls/component.mk
+++ b/components/esp-tls/component.mk
@@ -1,7 +1,12 @@
-ifdef CONFIG_SSL_USING_MBEDTLS
-COMPONENT_SRCDIRS := .
-COMPONENT_ADD_INCLUDEDIRS := .
-else
 COMPONENT_SRCDIRS :=
 COMPONENT_ADD_INCLUDEDIRS :=
+
+ifdef CONFIG_SSL_USING_MBEDTLS 
+COMPONENT_SRCDIRS := .
+COMPONENT_ADD_INCLUDEDIRS := .
 endif
+
+ifdef CONFIG_SSL_USING_WOLFSSL 
+COMPONENT_SRCDIRS := .
+COMPONENT_ADD_INCLUDEDIRS := .
+endif
\ No newline at end of file
diff --git a/components/esp-tls/esp_tls.c b/components/esp-tls/esp_tls.c
index b97f4ebc..039dc31a 100644
--- a/components/esp-tls/esp_tls.c
+++ b/components/esp-tls/esp_tls.c
@@ -28,7 +28,12 @@
 #endif
 
 static const char *TAG = "esp-tls";
+#if CONFIG_SSL_USING_MBEDTLS
 static mbedtls_x509_crt *global_cacert = NULL;
+#else
+static unsigned char *global_cacert = NULL;
+static unsigned int global_cacert_pem_bytes = 0;
+#endif
 
 #ifdef ESP_PLATFORM
 #include <esp_log.h>
@@ -67,6 +72,7 @@ static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
 
 static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen)
 {
+#if CONFIG_SSL_USING_MBEDTLS
     ssize_t ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)data, datalen);   
     if (ret < 0) {
         if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
@@ -76,6 +82,20 @@ static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen)
             ESP_LOGE(TAG, "read error :%d:", ret);
         }
     }
+#else
+    size_t ret = wolfSSL_read(tls->ssl, (unsigned char *)data, datalen);
+    if (ret < 0) {
+        ret = wolfSSL_get_error(tls->ssl, ret);
+        /* peer sent close notify */
+        if (ret == WOLFSSL_ERROR_ZERO_RETURN) {
+            return 0;
+        }
+
+        if (ret != WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_ERROR_WANT_WRITE) {
+            ESP_LOGE(TAG, "read error :%d:", ret);
+        }
+    }
+#endif
     return ret;
 }
 
@@ -146,12 +166,14 @@ err_freeaddr:
     return ret;
 }
 
+
 esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes)
 {
     if (cacert_pem_buf == NULL) {
         ESP_LOGE(TAG, "cacert_pem_buf is null");
         return ESP_ERR_INVALID_ARG;
     }
+#if CONFIG_SSL_USING_MBEDTLS
     if (global_cacert != NULL) {
         mbedtls_x509_crt_free(global_cacert);
     }
@@ -171,23 +193,43 @@ esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const
         ESP_LOGE(TAG, "mbedtls_x509_crt_parse was partly successful. No. of failed certificates: %d", ret);
     }
     return ESP_OK;
+#else
+    if (global_cacert != NULL) {
+        esp_tls_free_global_ca_store(global_cacert);
+    }
+
+    global_cacert = (unsigned char *)strndup((const char *)cacert_pem_buf, cacert_pem_bytes);
+    if (!global_cacert)
+        return ESP_FAIL;
+
+    global_cacert_pem_bytes = cacert_pem_bytes;
+
+    return ESP_OK;
+#endif
 }
 
-mbedtls_x509_crt *esp_tls_get_global_ca_store()
+void *esp_tls_get_global_ca_store()
 {
-    return global_cacert;
+    return (void*)global_cacert;
 }
 
 void esp_tls_free_global_ca_store()
 {
     if (global_cacert) {
+#if CONFIG_SSL_USING_MBEDTLS
         mbedtls_x509_crt_free(global_cacert);
         global_cacert = NULL;
+#else
+        free(global_cacert);
+        global_cacert = NULL;
+        global_cacert_pem_bytes = 0;
+#endif
     }
 }
 
 static void verify_certificate(esp_tls_t *tls)
 {
+#if CONFIG_SSL_USING_MBEDTLS
     int flags;
     char buf[100];
     if ((flags = mbedtls_ssl_get_verify_result(&tls->ssl)) != 0) {
@@ -198,13 +240,22 @@ static void verify_certificate(esp_tls_t *tls)
     } else {
         ESP_LOGI(TAG, "Certificate verified.");
     }
+#else
+    int flags;
+    if ((flags = wolfSSL_get_verify_result(tls->ssl)) != WOLFSSL_SUCCESS) {
+        ESP_LOGE(TAG, "Failed to verify peer certificate %d!", flags);
+    } else {
+        ESP_LOGI(TAG, "Certificate verified.");
+    }
+#endif
 }
 
-static void mbedtls_cleanup(esp_tls_t *tls) 
+static void esp_tls_cleanup(esp_tls_t *tls)
 {
     if (!tls) {
         return;
     }
+#if CONFIG_SSL_USING_MBEDTLS
     if (tls->cacert_ptr != global_cacert) {
         mbedtls_x509_crt_free(tls->cacert_ptr);
     }
@@ -217,12 +268,19 @@ static void mbedtls_cleanup(esp_tls_t *tls)
     mbedtls_ctr_drbg_free(&tls->ctr_drbg);
     mbedtls_ssl_free(&tls->ssl);
     mbedtls_net_free(&tls->server_fd);
+#else
+    wolfSSL_shutdown(tls->ssl);
+    wolfSSL_free(tls->ssl);
+    close(tls->sockfd);
+    wolfSSL_CTX_free(tls->ctx);
+    wolfSSL_Cleanup();
+#endif
 }
 
 static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostlen, const esp_tls_cfg_t *cfg)
 {
     int ret;
-    
+#if CONFIG_SSL_USING_MBEDTLS
     mbedtls_net_init(&tls->server_fd);
     tls->server_fd.fd = tls->sockfd;
     mbedtls_ssl_init(&tls->ssl);
@@ -326,8 +384,74 @@ static int create_ssl_handle(esp_tls_t *tls, const char *hostname, size_t hostle
 
     return 0;
 exit:
-    mbedtls_cleanup(tls);
+    esp_tls_cleanup(tls);
     return -1;
+#else
+    ret = wolfSSL_Init();
+    if (ret != WOLFSSL_SUCCESS) {
+        ESP_LOGE(TAG, "Init wolfSSL failed: %d", ret);
+        goto exit;
+    }
+
+    tls->ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
+    if (!tls->ctx) {
+        ESP_LOGE(TAG, "Set wolfSSL ctx failed");
+        goto exit;
+    }
+
+#ifdef HAVE_ALPN
+    if (cfg->alpn_protos) {
+        char **alpn_list = (char **)cfg->alpn_protos;
+        for (; *alpn_list != NULL; alpn_list ++) {
+            if (wolfSSL_UseALPN(tls->ssl, *alpn_list, strlen(*alpn_list), WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) {
+                ESP_LOGE(TAG, "Use wolfSSL ALPN failed");
+                goto exit;
+            }
+        }
+    }
+#endif
+
+    if (cfg->use_global_ca_store == true) {
+        wolfSSL_CTX_load_verify_buffer(tls->ctx, global_cacert, global_cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
+        wolfSSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER, NULL);
+    } else if (cfg->cacert_pem_buf != NULL) {
+        wolfSSL_CTX_load_verify_buffer(tls->ctx, cfg->cacert_pem_buf, cfg->cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
+        wolfSSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER, NULL);
+    } else {
+        wolfSSL_CTX_set_verify(tls->ctx, SSL_VERIFY_NONE, NULL);
+    }
+
+    if (cfg->clientcert_pem_buf != NULL && cfg->clientkey_pem_buf != NULL) {
+        wolfSSL_CTX_use_certificate_buffer(tls->ctx, cfg->clientcert_pem_buf, cfg->clientcert_pem_bytes, WOLFSSL_FILETYPE_PEM);
+        wolfSSL_CTX_use_PrivateKey_buffer(tls->ctx, cfg->clientkey_pem_buf, cfg->clientkey_pem_bytes, WOLFSSL_FILETYPE_PEM);
+    } else if (cfg->clientcert_pem_buf != NULL || cfg->clientkey_pem_buf != NULL) {
+        ESP_LOGE(TAG, "You have to provide both clientcert_pem_buf and clientkey_pem_buf for mutual authentication\n\n");
+        goto exit;
+    }
+
+    tls->ssl = wolfSSL_new(tls->ctx);
+    if (!tls->ssl) {
+        ESP_LOGE(TAG, "Create wolfSSL failed");
+        goto exit;
+    }
+
+#ifdef HAVE_SNI
+    /* Hostname set here should match CN in server certificate */
+    char *use_host = strndup(hostname, hostlen);
+    if (!use_host) {
+        goto exit;
+    }
+    wolfSSL_set_tlsext_host_name(tls->ssl, use_host);
+    free(use_host);
+#endif
+
+    wolfSSL_set_fd(tls->ssl, tls->sockfd);
+
+    return 0;
+exit:
+    esp_tls_cleanup(tls);
+    return -1;
+#endif
 }
 
 /**
@@ -336,7 +460,7 @@ exit:
 void esp_tls_conn_delete(esp_tls_t *tls)
 {
     if (tls != NULL) {
-        mbedtls_cleanup(tls);
+        esp_tls_cleanup(tls);
         if (tls->sockfd) {
             close(tls->sockfd);
         }
@@ -351,6 +475,7 @@ static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
 
 static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen)
 {
+#if CONFIG_SSL_USING_MBEDTLS
     ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data, datalen);
     if (ret < 0) {
         if (ret != MBEDTLS_ERR_SSL_WANT_READ  && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
@@ -358,6 +483,15 @@ static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen)
         }
     }
     return ret;
+#else
+    ssize_t ret = wolfSSL_write(tls->ssl, (unsigned char*) data, datalen);
+    if (ret < 0) {
+        if (ret != WOLFSSL_ERROR_WANT_READ  && ret != WOLFSSL_ERROR_WANT_WRITE) {
+            ESP_LOGE(TAG, "write error :%d:", ret);
+        }
+    }
+    return ret;
+#endif
 }
 
 static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
@@ -427,6 +561,7 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
             /* falls through */
         case ESP_TLS_HANDSHAKE:
             ESP_LOGD(TAG, "handshake in progress...");
+#if CONFIG_SSL_USING_MBEDTLS
             ret = mbedtls_ssl_handshake(&tls->ssl);
             if (ret == 0) {
                 tls->conn_state = ESP_TLS_DONE;
@@ -445,6 +580,26 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
                    or MBEDTLS_ERR_SSL_WANT_WRITE during handshake */
                 return 0;
             }
+#else
+            ret = wolfSSL_connect(tls->ssl);
+            if (ret == WOLFSSL_SUCCESS) {
+                tls->conn_state = ESP_TLS_DONE;
+                return 1;
+            } else {
+                if (ret != WOLFSSL_ERROR_WANT_READ && ret != WOLFSSL_ERROR_WANT_WRITE) {
+                    ESP_LOGE(TAG, "wolfSSL_connect returned -0x%x", -ret);
+                    if (cfg->cacert_pem_buf != NULL || cfg->use_global_ca_store == true) {
+                        /* This is to check whether handshake failed due to invalid certificate*/
+                        verify_certificate(tls);
+                    }
+                    tls->conn_state = ESP_TLS_FAIL;
+                    return -1;
+                }
+                /* Irrespective of blocking or non-blocking I/O, we return on getting wolfSSL_want_read
+                   or wolfSSL_want_write during handshake */
+                return 0;
+            }
+#endif
             break;
         case ESP_TLS_FAIL:
             ESP_LOGE(TAG, "failed to open a new connection");;
@@ -490,9 +645,13 @@ int esp_tls_conn_new_async(const char *hostname, int hostlen, int port, const es
 
 size_t esp_tls_get_bytes_avail(esp_tls_t *tls)
 {
+#if CONFIG_SSL_USING_MBEDTLS
     if (!tls) {
         ESP_LOGE(TAG, "empty arg passed to esp_tls_get_bytes_avail()");
         return ESP_FAIL;
     }
     return mbedtls_ssl_get_bytes_avail(&tls->ssl);
+#else
+    return 0;
+#endif
 }
diff --git a/components/esp-tls/esp_tls.h b/components/esp-tls/esp_tls.h
index 3782c0e9..246d7e81 100644
--- a/components/esp-tls/esp_tls.h
+++ b/components/esp-tls/esp_tls.h
@@ -18,6 +18,7 @@
 #include <sys/socket.h>
 #include <fcntl.h>
 
+#if CONFIG_SSL_USING_MBEDTLS
 #include "mbedtls/platform.h"
 #include "mbedtls/net_sockets.h"
 #include "mbedtls/esp_debug.h"
@@ -26,6 +27,9 @@
 #include "mbedtls/ctr_drbg.h"
 #include "mbedtls/error.h"
 #include "mbedtls/certs.h"
+#else
+#include "wolfssl/ssl.h"
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -90,6 +94,7 @@ typedef struct esp_tls_cfg {
  * @brief      ESP-TLS Connection Handle 
  */
 typedef struct esp_tls {
+#if CONFIG_SSL_USING_MBEDTLS
     mbedtls_ssl_context ssl;                                                    /*!< TLS/SSL context */
  
     mbedtls_entropy_context entropy;                                            /*!< mbedTLS entropy context structure */
@@ -112,7 +117,10 @@ typedef struct esp_tls {
 
     mbedtls_pk_context clientkey;                                               /*!< Container for the private key of the client
                                                                                      certificate */
-
+#else
+    WOLFSSL_CTX *ctx;
+    WOLFSSL     *ssl;
+#endif
     int sockfd;                                                                 /*!< Underlying socket file descriptor. */
  
     ssize_t (*esp_tls_read)(struct esp_tls  *tls, char *data, size_t datalen);          /*!< Callback function for reading data from TLS/SSL
@@ -258,7 +266,7 @@ esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const
  *             - Pointer to the global CA store currently being used    if successful.
  *             - NULL                                                   if there is no global CA store set.
  */
-mbedtls_x509_crt *esp_tls_get_global_ca_store();
+void *esp_tls_get_global_ca_store();
 
 /**
  * @brief      Free the global CA store currently being used.