Skip to main content

Mbed TLS

Mbed TLS is the cryptography and TLS library ST integrates through CubeMX. It is usable on parts with enough memory — budget at least 60 KB flash and 64 KB SRAM for a minimal build, more once a full TLS handshake is involved.

The examples below use SHA-1 because it is the smallest thing to wire up and verify. SHA-1 is broken for signatures and certificates; use SHA-256 or better in real code. It survives here only as a self-contained "is the library linked and running" check.

With STM32CubeMX

Enable, in the .ioc:

  • RNG — Mbed TLS needs an entropy source, and the hardware RNG is it.
  • Mbed TLS under the middleware list.

Then set the modes:

  • MBEDTLS_SSL_CLI_C: Defined — TLS client support.
  • MBEDTLS_SHA1_C: Defined — for the example below.

If the RTC is disabled, turn off the time-dependent features, or the build pulls in gettimeofday and fails to link:

  • MBEDTLS_HAVE_TIME: Not Defined
  • MBEDTLS_HAVE_TIME_DATE: Not Defined

Certificate validity checking needs a real clock. Leaving time disabled is fine for hashing or for a client that pins a key, but a full certificate chain validation cannot check expiry without it — enable the RTC and set it before the first TLS session in that case.

Example

/* USER CODE BEGIN Includes */
#include "mbedtls/sha1.h"
/* USER CODE END Includes */

void StartDefaultTask(void const * argument)
{
MX_USB_HOST_Init();
/* USER CODE BEGIN 5 */
for (;;)
{
osDelay(10000);

printf("Mbed TLS port on STM32F746G-DISCO board\r\n");

const char *source_cxt = "Rojar Smith";
unsigned char digest[20]; /* SHA-1 is exactly 20 bytes */

printf("source context is: %s\r\n", source_cxt);

mbedtls_sha1_context sha1_ctx;
mbedtls_sha1_init(&sha1_ctx);
mbedtls_sha1_starts(&sha1_ctx);
mbedtls_sha1_update(&sha1_ctx, (const unsigned char *)source_cxt, strlen(source_cxt));
mbedtls_sha1_finish(&sha1_ctx, digest);
mbedtls_sha1_free(&sha1_ctx);

printf("sha1 digest is: [");
for (int i = 0; i < 20; i++) { /* < 20, not <= 20 */
printf("%02x", digest[i]);
}
printf("]\r\n");
/* Expected: 82b1e6dc4fb9b011b4ecae116f694359d262ebc5 */
}
/* USER CODE END 5 */
}

Two corrections over the naive version of this loop:

  • The digest buffer must be unsigned char. Printing a signed char with %02x sign-extends any byte ≥ 0x80 to ffffffxx, so the hex string comes out wrong for exactly the inputs that matter.
  • The loop bound is i < 20. SHA-1 produces 20 bytes; i <= 20 reads and prints one byte past the end.

printf here goes to the UART — see UART and virtual COM port for the retarget.

Stack and heap

Give the RTOS default task and the heap room, or the handshake overflows a stack it shares with cryptographic scratch buffers:

Minimum Heap Size: 0x1000
Minimum Stack Size: 0x1000

These are _Min_Heap_Size / _Min_Stack_Size in the linker script; CubeMX exposes them under Project Manager ▸ Linker Settings. A full TLS session needs considerably more than a bare hash — several kilobytes of stack for the handshake alone.

A recent STM32CubeIDE toolchain reporting an unresolved _gettimeofday_r means a time-dependent Mbed TLS feature is still enabled while newlib-nano provides no gettimeofday stub. Either turn off MBEDTLS_HAVE_TIME and MBEDTLS_HAVE_TIME_DATE as above, or provide a _gettimeofday implementation backed by the RTC. Editing the linker script does not help — the missing symbol is a syscall stub, not a memory-layout problem.

Manual integration

When not using the CubeMX middleware, add the sources by hand:

  • Copy include/ and library/ into the project.

  • Copy one config header from configs/ into include/.

  • Add the library/*.c files as linked sources, and add the include paths.

  • Add the config-file preprocessor symbol:

    MBEDTLS_CONFIG_FILE=<config-mini-tls1_1.h>

    The angle brackets are part of the value — Mbed TLS uses them in the #include MBEDTLS_CONFIG_FILE directive, so define it literally as MBEDTLS_CONFIG_FILE=<config-mini-tls1_1.h>.

Trim the chosen config to what a microcontroller has. In config-mini-tls1_1.h:

//#define MBEDTLS_HAVE_TIME
//#define MBEDTLS_HAVE_TIME_DATE
//#define MBEDTLS_NET_C /* no BSD sockets on bare metal */
//#define MBEDTLS_SHA1_C /* enable only what you use */

/* No OS entropy source; feed the hardware RNG instead */
#define MBEDTLS_NO_PLATFORM_ENTROPY

MBEDTLS_NET_C assumes a POSIX socket layer, which bare-metal targets do not have — leave it off and supply your own send/receive callbacks. MBEDTLS_NO_PLATFORM_ENTROPY tells Mbed TLS there is no OS entropy pool, so you must register a hardware entropy source (the RNG) with mbedtls_entropy_add_source.

Validate the configuration

Add this at the end of the config header. It fails the compile with a clear message if the selected options are inconsistent, which is far easier to debug than a link error later:

#include "mbedtls/check_config.h"