Hello world example of a TLS client: fetch an HTTPS page. The canonical source for this example lives at https://github.com/ARMmbed/mbed-os-example-tls

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HelloHttpsClient.cpp Source File

HelloHttpsClient.cpp

00001 /*
00002  *  Hello world example of a TLS client: fetch an HTTPS page
00003  *
00004  *  Copyright (C) 2006-2018, Arm Limited, All Rights Reserved
00005  *  SPDX-License-Identifier: Apache-2.0
00006  *
00007  *  Licensed under the Apache License, Version 2.0 (the "License"); you may
00008  *  not use this file except in compliance with the License.
00009  *  You may obtain a copy of the License at
00010  *
00011  *  http://www.apache.org/licenses/LICENSE-2.0
00012  *
00013  *  Unless required by applicable law or agreed to in writing, software
00014  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00015  *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00016  *  See the License for the specific language governing permissions and
00017  *  limitations under the License.
00018  *
00019  *  This file is part of Mbed TLS (https://tls.mbed.org)
00020  */
00021 
00022 #include "HelloHttpsClient.h"
00023 
00024 #include "mbedtls/platform.h"
00025 #include "mbedtls/config.h"
00026 #include "mbedtls/ssl.h"
00027 #include "mbedtls/entropy.h"
00028 #include "mbedtls/ctr_drbg.h"
00029 #include "mbedtls/error.h"
00030 #include "mbedtls/debug.h"
00031 #include "mbedtls/x509.h"
00032 
00033 #include <stdint.h>
00034 #include <string.h>
00035 #include "mbed.h"
00036 
00037 const char *HelloHttpsClient::DRBG_PERSONALIZED_STR =
00038                                                 "Mbed TLS helloword client";
00039 
00040 const size_t HelloHttpsClient::ERROR_LOG_BUFFER_LENGTH = 128;
00041 
00042 const char *HelloHttpsClient::TLS_PEM_CA =
00043     "-----BEGIN CERTIFICATE-----\n"
00044     "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n"
00045     "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n"
00046     "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n"
00047     "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n"
00048     "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n"
00049     "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n"
00050     "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n"
00051     "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n"
00052     "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n"
00053     "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n"
00054     "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n"
00055     "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n"
00056     "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n"
00057     "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n"
00058     "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n"
00059     "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n"
00060     "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n"
00061     "rqXRfboQnoZsG4q5WTP468SQvvG5\n"
00062     "-----END CERTIFICATE-----\n";
00063 
00064 const char *HelloHttpsClient::HTTP_REQUEST_FILE_PATH =
00065                                     "/media/uploads/mbed_official/hello.txt";
00066 
00067 const char *HelloHttpsClient::HTTP_HELLO_STR = "Hello world!";
00068 
00069 const char *HelloHttpsClient::HTTP_OK_STR = "200 OK";
00070 
00071 HelloHttpsClient::HelloHttpsClient(const char *in_server_name,
00072                                    const char *in_server_addr,
00073                                    const uint16_t in_server_port) :
00074     socket(),
00075     server_name(in_server_name),
00076     server_addr(in_server_addr),
00077     server_port(in_server_port)
00078 {
00079     mbedtls_entropy_init(&entropy);
00080     mbedtls_ctr_drbg_init(&ctr_drbg);
00081     mbedtls_x509_crt_init(&cacert);
00082     mbedtls_ssl_init(&ssl);
00083     mbedtls_ssl_config_init(&ssl_conf);
00084 }
00085 
00086 HelloHttpsClient::~HelloHttpsClient()
00087 {
00088     mbedtls_entropy_free(&entropy);
00089     mbedtls_ctr_drbg_free(&ctr_drbg);
00090     mbedtls_x509_crt_free(&cacert);
00091     mbedtls_ssl_free(&ssl);
00092     mbedtls_ssl_config_free(&ssl_conf);
00093 
00094     socket.close();
00095 }
00096 
00097 int HelloHttpsClient::run()
00098 {
00099     int ret;
00100     size_t req_len, req_offset, resp_offset;
00101     uint32_t flags;
00102     bool resp_200, resp_hello;
00103 
00104     /* Configure the TCPSocket */
00105     if ((ret = configureTCPSocket()) != 0)
00106         return ret;
00107 
00108     /* Configure already initialized Mbed TLS structures */
00109     if ((ret = configureTlsContexts()) != 0)
00110         return ret;
00111 
00112     /* Start a connection to the server */
00113     if ((ret = socket.connect(server_addr, server_port)) != NSAPI_ERROR_OK) {
00114         mbedtls_printf("socket.connect() returned %d\n", ret);
00115         return ret;
00116     }
00117     mbedtls_printf("Successfully connected to %s at port %u\n",
00118                    server_addr, server_port);
00119 
00120     /* Start the TLS handshake */
00121     mbedtls_printf("Starting the TLS handshake...\n");
00122     do {
00123         ret = mbedtls_ssl_handshake(&ssl);
00124     } while(ret != 0 &&
00125             (ret == MBEDTLS_ERR_SSL_WANT_READ ||
00126             ret == MBEDTLS_ERR_SSL_WANT_WRITE));
00127     if (ret < 0) {
00128         mbedtls_printf("mbedtls_ssl_handshake() returned -0x%04X\n", -ret);
00129         return ret;
00130     }
00131     mbedtls_printf("Successfully completed the TLS handshake\n");
00132 
00133     /* Fill the request buffer */
00134     ret = snprintf(gp_buf, sizeof(gp_buf),
00135                    "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", HTTP_REQUEST_FILE_PATH,
00136                    server_name);
00137     req_len = static_cast<size_t>(ret);
00138     if (ret < 0 || req_len >= sizeof(gp_buf)) {
00139         mbedtls_printf("Failed to compose HTTP request using snprintf: %d\n",
00140                        ret);
00141         return ret;
00142     }
00143 
00144     /* Send the HTTP request to the server over TLS */
00145     req_offset = 0;
00146     do {
00147         ret = mbedtls_ssl_write(&ssl,
00148                 reinterpret_cast<const unsigned char *>(gp_buf + req_offset),
00149                 req_len - req_offset);
00150         if (ret > 0)
00151             req_offset += static_cast<size_t>(ret);
00152     }
00153     while(req_offset < req_len &&
00154           (ret > 0 ||
00155           ret == MBEDTLS_ERR_SSL_WANT_WRITE ||
00156           ret == MBEDTLS_ERR_SSL_WANT_READ));
00157     if (ret < 0) {
00158         mbedtls_printf("mbedtls_ssl_write() returned -0x%04X\n", -ret);
00159         return ret;
00160     }
00161 
00162     /* Print information about the TLS connection */
00163     ret = mbedtls_x509_crt_info(gp_buf, sizeof(gp_buf),
00164                                 "\r  ", mbedtls_ssl_get_peer_cert(&ssl));
00165     if (ret < 0) {
00166         mbedtls_printf("mbedtls_x509_crt_info() returned -0x%04X\n", -ret);
00167         return ret;
00168     }
00169     mbedtls_printf("Server certificate:\n%s\n", gp_buf);
00170 
00171     /* Ensure certificate verification was successful */
00172     flags = mbedtls_ssl_get_verify_result(&ssl);
00173     if (flags != 0) {
00174         ret = mbedtls_x509_crt_verify_info(gp_buf, sizeof(gp_buf),
00175                                            "\r  ! ", flags);
00176         if (ret < 0) {
00177             mbedtls_printf("mbedtls_x509_crt_verify_info() returned "
00178                            "-0x%04X\n", -ret);
00179             return ret;
00180         } else {
00181             mbedtls_printf("Certificate verification failed (flags %lu):"
00182                            "\n%s\n", flags, gp_buf);
00183             return -1;
00184         }
00185     } else {
00186         mbedtls_printf("Certificate verification passed\n");
00187     }
00188 
00189     mbedtls_printf("Established TLS connection to %s\n", server_name);
00190 
00191     /* Read response from the server */
00192     resp_offset = 0;
00193     resp_200 = false;
00194     resp_hello = false;
00195     do {
00196         ret = mbedtls_ssl_read(&ssl,
00197                     reinterpret_cast<unsigned char *>(gp_buf  + resp_offset),
00198                     sizeof(gp_buf) - resp_offset - 1);
00199         if (ret > 0)
00200             resp_offset += static_cast<size_t>(ret);
00201 
00202         /* Ensure that the response string is null-terminated */
00203         gp_buf[resp_offset] = '\0';
00204 
00205         /* Check  if we received expected string */
00206         resp_200 = resp_200 || strstr(gp_buf, HTTP_OK_STR) != NULL;
00207         resp_hello = resp_hello || strstr(gp_buf, HTTP_HELLO_STR) != NULL;
00208     } while((!resp_200 || !resp_hello) &&
00209             (ret > 0 ||
00210             ret == MBEDTLS_ERR_SSL_WANT_READ || MBEDTLS_ERR_SSL_WANT_WRITE));
00211     if (ret < 0) {
00212         mbedtls_printf("mbedtls_ssl_read() returned -0x%04X\n", -ret);
00213         return ret;
00214     }
00215 
00216     /* Display response information */
00217     mbedtls_printf("HTTP: Received %u chars from server\n", resp_offset);
00218     mbedtls_printf("HTTP: Received '%s' status ... %s\n", HTTP_OK_STR,
00219                    resp_200 ? "OK" : "FAIL");
00220     mbedtls_printf("HTTP: Received message:\n%s\n", gp_buf);
00221 
00222     return 0;
00223 }
00224 
00225 int HelloHttpsClient::configureTCPSocket()
00226 {
00227     int ret;
00228 
00229     NetworkInterface *network = NetworkInterface::get_default_instance();
00230     if(network == NULL) {
00231         mbedtls_printf("ERROR: No network interface found!\n");
00232         return -1;
00233     }
00234     ret = network->connect();
00235     if (ret != 0) {
00236         mbedtls_printf("Error! network->connect() returned: %d\n", ret);
00237         return ret;
00238     }
00239 
00240     if ((ret = socket.open(network)) != NSAPI_ERROR_OK) {
00241         mbedtls_printf("socket.open() returned %d\n", ret);
00242         return ret;
00243     }
00244 
00245     socket.set_blocking(false);
00246 
00247     return 0;
00248 }
00249 
00250 int HelloHttpsClient::configureTlsContexts()
00251 {
00252     int ret;
00253 
00254     ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
00255             reinterpret_cast<const unsigned char *>(DRBG_PERSONALIZED_STR),
00256             strlen(DRBG_PERSONALIZED_STR) + 1);
00257     if (ret != 0) {
00258         mbedtls_printf("mbedtls_ctr_drbg_seed() returned -0x%04X\n", -ret);
00259         return ret;
00260     }
00261 
00262     ret = mbedtls_x509_crt_parse(&cacert,
00263                         reinterpret_cast<const unsigned char *>(TLS_PEM_CA),
00264                         strlen(TLS_PEM_CA) + 1);
00265     if (ret != 0) {
00266         mbedtls_printf("mbedtls_x509_crt_parse() returned -0x%04X\n", -ret);
00267         return ret;
00268     }
00269 
00270     ret = mbedtls_ssl_config_defaults(&ssl_conf, MBEDTLS_SSL_IS_CLIENT,
00271                                       MBEDTLS_SSL_TRANSPORT_STREAM,
00272                                       MBEDTLS_SSL_PRESET_DEFAULT);
00273     if (ret != 0) {
00274         mbedtls_printf("mbedtls_ssl_config_defaults() returned -0x%04X\n",
00275                        -ret);
00276         return ret;
00277     }
00278 
00279     mbedtls_ssl_conf_ca_chain(&ssl_conf, &cacert, NULL);
00280     mbedtls_ssl_conf_rng(&ssl_conf, mbedtls_ctr_drbg_random, &ctr_drbg);
00281 
00282     /*
00283      * It is possible to disable authentication by passing
00284      * MBEDTLS_SSL_VERIFY_NONE in the call to mbedtls_ssl_conf_authmode()
00285      */
00286     mbedtls_ssl_conf_authmode(&ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
00287 
00288     /* Configure certificate verification function to clear time/date flags */
00289     mbedtls_ssl_conf_verify(&ssl_conf, sslVerify, this);
00290 
00291 #if HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0
00292     mbedtls_ssl_conf_dbg(&ssl_conf, sslDebug, NULL);
00293     mbedtls_debug_set_threshold(HELLO_HTTPS_CLIENT_DEBUG_LEVEL);
00294 #endif /* HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 */
00295 
00296     if ((ret = mbedtls_ssl_setup( &ssl, &ssl_conf)) != 0) {
00297         mbedtls_printf("mbedtls_ssl_setup() returned -0x%04X\n", -ret);
00298         return ret;
00299     }
00300 
00301     if ((ret = mbedtls_ssl_set_hostname( &ssl, server_name )) != 0) {
00302         mbedtls_printf("mbedtls_ssl_set_hostname() returned -0x%04X\n",
00303                        -ret);
00304         return ret;
00305     }
00306 
00307     mbedtls_ssl_set_bio(&ssl, static_cast<void *>(&socket), sslSend, sslRecv,
00308                         NULL);
00309 
00310     return 0;
00311 }
00312 
00313 int HelloHttpsClient::sslRecv(void *ctx, unsigned char *buf, size_t len)
00314 {
00315     TCPSocket *socket = static_cast<TCPSocket *>(ctx);
00316     int ret = socket->recv(buf, len);
00317 
00318     if (ret == NSAPI_ERROR_WOULD_BLOCK)
00319         ret = MBEDTLS_ERR_SSL_WANT_READ;
00320     else if (ret < 0)
00321         mbedtls_printf("socket.recv() returned %d\n", ret);
00322 
00323     return ret;
00324 }
00325 
00326 int HelloHttpsClient::sslSend(void *ctx, const unsigned char *buf, size_t len)
00327 {
00328     TCPSocket *socket = static_cast<TCPSocket *>(ctx);
00329     int ret = socket->send(buf, len);
00330 
00331     if (ret == NSAPI_ERROR_WOULD_BLOCK)
00332         ret = MBEDTLS_ERR_SSL_WANT_WRITE;
00333     else if (ret < 0)
00334         mbedtls_printf("socket.send() returned %d\n", ret);
00335 
00336     return ret;
00337 }
00338 
00339 void HelloHttpsClient::sslDebug(void *ctx, int level, const char *file,
00340                                 int line, const char *str)
00341 {
00342     (void)ctx;
00343 
00344     const char *p, *basename;
00345 
00346     /* Extract basename from file */
00347     for (p = basename = file; *p != '\0'; p++) {
00348         if (*p == '/' || *p == '\\')
00349             basename = p + 1;
00350     }
00351 
00352     mbedtls_printf("%s:%d: |%d| %s\r", basename, line, level, str);
00353 }
00354 
00355 int HelloHttpsClient::sslVerify(void *ctx, mbedtls_x509_crt *crt, int depth,
00356                                 uint32_t *flags)
00357 {
00358     int ret = 0;
00359 
00360     /*
00361      * If MBEDTLS_HAVE_TIME_DATE is defined, then the certificate date and time
00362      * validity checks will probably fail because this application does not set
00363      * up the clock correctly. We filter out date and time related failures
00364      * instead
00365      */
00366     *flags &= ~MBEDTLS_X509_BADCERT_FUTURE & ~MBEDTLS_X509_BADCERT_EXPIRED;
00367 
00368 #if HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0
00369     HelloHttpsClient *client = static_cast<HelloHttpsClient *>(ctx);
00370 
00371     ret = mbedtls_x509_crt_info(client->gp_buf, sizeof(gp_buf), "\r  ", crt);
00372     if (ret < 0) {
00373         mbedtls_printf("mbedtls_x509_crt_info() returned -0x%04X\n", -ret);
00374     } else {
00375         ret = 0;
00376         mbedtls_printf("Verifying certificate at depth %d:\n%s\n",
00377                        depth, client->gp_buf);
00378     }
00379 #endif /* HELLO_HTTPS_CLIENT_DEBUG_LEVEL > 0 */
00380 
00381     return ret;
00382 }