Master The Pico WiFi: Simplest HTTPS Client
Written by Harry Fairhead & Mike James   
Monday, 01 May 2023
Article Index
Master The Pico WiFi: Simplest HTTPS Client
SSL and TLS
A Simple TLS Client
Listing

A Simple TLS Client

A good starting point in getting to grips with mbedtls is to convert the simplest HTTP client program given in Chapter 2 to HTTPS. The simplicity of this program lets you see where the changes have to be made.

What might surprise you is how easy the conversion to HTTPS is. All you have to do to the main program is add:

#include "lwip/altcp_tls.h"

and replace the line which constructs the standard TCP PCB:

struct altcp_pcb *pcb = altcp_new(NULL);

by:

struct altcp_tls_config *tls_config =
altcp_tls_create_config_client(NULL, 0);
struct altcp_pcb *pcb = altcp_tls_new(tls_config,
IPADDR_TYPE_ANY);

The altcp_tls_create_config_client creates an additional struct to be added to the usual PCB. Usually the first parameter passed is the certificate and the second its length. In this case the client doesn’t need a certificate so NULL and 0 are appropriate arguments. Once the config has been created we can create the extended PCB which includes the details of the TSL connection.

The only other instruction the program needs is to tell the server which certificate it should use:

mbedtls_ssl_set_hostname(altcp_tls_context(pcb), 
"example.com");

A single server at a given IP address may be hosting more than one HTTPS site, each with its own certificate. We have to tell the server which website we want to allow it to use the correct certificate.

We also have to change the port being used for the connection:

err_t err = altcp_connect(pcb, &ip, 443, connected);

With these changes and additions the program will just work as before, but with an HTTPS connection. However, as the program stands it will not compile because we need to make changes to the configuration files. We need to add:

#undef TCP_WND
#define TCP_WND  16384
#define LWIP_ALTCP               1
#define LWIP_ALTCP_TLS           1
#define LWIP_ALTCP_TLS_MBEDTLS   1
#define LWIP_DEBUG 1
#define ALTCP_MBEDTLS_DEBUG  LWIP_DBG_ON
#endif /* __LWIPOPTS_H__ */

to the end of the usual lwipopts.h (the one obtained from pico/pico-examples/pico_w/lwipopts_examples_common.h).

The TCP_WND needs to be increased because TLS is more demanding. The others enable altcp, altcp_tls and mbedtls as part of the build.

As already mentioned, when using mbedtls we also need a new configuration file, mbedtls_config.h. This one overrides the default configuration file and you need to set a number of configuration features to make mbedtls work at all. There are a huge number of parameters you can change and they are poorly documented and difficult to understand by anyone who isn’t an expert in encryption methods. As the whole point of using an encryption library is to avoid having to be an expert this is disappointing. However, it is possible to make sense of the different aspects of configuration. Rather than simply presenting a standard configuration file, we can construct a minimal configuration file and explain what the defines are actually doing.

The mbedtls library is mostly hardware-independent and will run on almost anything with few changes. The only hardware-specific configurations we need are:

//Hardware config
#define MBEDTLS_NO_PLATFORM_ENTROPY
#define MBEDTLS_ENTROPY_HARDWARE_ALT
#define MBEDTLS_HAVE_TIME

This first tells mbedtls that the Pico doesn’t have a source of entropy because it isn’t running an operating system. The second promises to provide a function that will return random data, i.e. entropy and the final one states the Pico supports time.h.

The source of randomness is in the form of a function:

int mbedtls_hardware_poll(void *data, unsigned char *output,
                                                              size_t len, size_t *olen)

This is supplied for us in the file:

pico/pico-sdk/src/rp2_common/pico_mbedtls/pico_mbedtls.c

This makes use of the new random functions added to Version 1.5 of the SDK, see the next chapter for more information.

After these three defines, the rest of the configuration file is concerned with which cryptographic methods you want to support. The problem here is trying to work out what to include and what to leave out. If you recall, a TLS connection involves a negotiation step which determines what key exchange protocol to use and what symmetric encryption method to use. The point is that you don’t need to support all of the possible methods, but if you don’t there will be the occasional connections that cannot be made.

So what key exchange and encryption methods should you support?

Although it is generally accepted that DHE_RSA is more secure, nearly every site supports RSA key exchange so this is what we will use for this first example. Most sites also support AES CCM or AES CBC as an encryption method.

Putting this together with the hardware requirements in a very simple mbedtls_config.h file:

//Hardware config
#define MBEDTLS_NO_PLATFORM_ENTROPY
#define MBEDTLS_ENTROPY_HARDWARE_ALT
#define MBEDTLS_HAVE_TIME
//error reporting #define MBEDTLS_ERROR_C
//used by lwIP #define MBEDTLS_ENTROPY_C #define MBEDTLS_CTR_DRBG_C
//RSA KEY EXCHANGE #define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED #define MBEDTLS_RSA_C
//general key exchange #define MBEDTLS_PKCS1_V15 #define MBEDTLS_BIGNUM_C #define MBEDTLS_PK_C #define MBEDTLS_PK_PARSE_C
//encryption #define MBEDTLS_AES_C #define MBEDTLS_CCM_C #define MBEDTLS_CIPHER_MODE_CBC #define MBEDTLS_AES_FEWER_TABLES
//certs #define MBEDTLS_X509_CRT_PARSE_C #define MBEDTLS_X509_USE_C #define MBEDTLS_OID_C #define MBEDTLS_ASN1_PARSE_C #define MBEDTLS_ASN1_WRITE_C
//hash methods #define MBEDTLS_SHA1_C #define MBEDTLS_SHA224_C #define MBEDTLS_SHA256_C #define MBEDTLS_SHA512_C

//TLS #define MBEDTLS_CIPHER_C #define MBEDTLS_SSL_TLS_C #define MBEDTLS_MD_C
//enable client and server modes and TLS #define MBEDTLS_SSL_CLI_C #define MBEDTLS_SSL_SERVER_NAME_INDICATION
//enable TLS 1.2 #define MBEDTLS_SSL_PROTO_TLS1_2
#include "/home/pi/pico/pico-sdk/lib/mbedtls/
include/mbedtls/check_config.h"

The configuration file has been divided into sections. The first is necessary because lwIP makes use of the features so they have to be included. The Key Exchange section specifies the RSA key exchange with a number of other defines that are dependencies. The encryption section enables AES with CBC and CCM, see the next chapter for information on what these are. The certificate handling and hash methods sections are standard. Next we turn on TLS, these defines have to be included to turn TLS on at all. We also define client mode and SNI, Server Name Indication, to send the server name to the server. Finally we enable TLS 1.2.

The very final line is worth knowing about this includes the check_config.h file which will run a check on the rest of the configuration. It will report any settings which don’t have all of their dependent settings enabled and it will report any conflicts. It makes creating an mbedtls_config.h file much easier than just trying to find out what everything does by reading the comments in the config.h file in /pico/pico-sdk/lib/mbedtls/include/mbedtls/. Of course, you need to modify the path so that it can be found.

It is worth knowing that modifying the configuration file needs a clean rebuild.

The only other file we need to modify is the CmakeLists.txt file:

cmake_minimum_required(VERSION 3.13)
set(PICO_BOARD pico_w)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
include(pico_sdk_import.cmake)
project(PicoW C CXX ASM)
pico_sdk_init()
add_executable(main
 main.c
)
target_include_directories(main
PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(main pico_stdlib
pico_cyw43_arch_lwip_threadsafe_background
pico_lwip_mbedtls pico_mbedtls)
pico_add_extra_outputs(main)

All we really have to do is add pico_lwip_mbedtls and pico_mbedtls to the target libraries.



Last Updated ( Tuesday, 02 May 2023 )