Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ WOLFCOSE_REF ?= main
WOLFTPM_REF ?= master
DOCKER_CMD=DOCKER_BUILDKIT=1 docker build $(DOCKER_CMD_EXTRA_ARGS) -t doc_build --build-arg MANPATH=$(MANPATH) --build-arg PDFFILE=$(PDFFILE) --build-arg V=$(V) --build-arg WOLFCOSE_REF=$(WOLFCOSE_REF) --build-arg WOLFTPM_REF=$(WOLFTPM_REF) --target=manual --output=build -f Dockerfile .

all: wolfssl wolfssh wolfboot wolfclu wolfcrypt-jni wolfmqtt wolfsentry wolfssl-jni wolftpm wolfhsm wolfcose wolfengine wolfprovider fips-ready tuning porting faq fips-faq bc-migration
all: wolfssl wolfssh wolfboot wolfclu wolfcrypt-jni wolfmqtt wolfsentry wolfssl-jni wolftpm wolfhsm wolfcose wolfengine wolfprovider fips-ready tuning porting faq fips-faq bc-migration mbedtls-migration

build:
$(Q)mkdir -p build
Expand Down Expand Up @@ -134,5 +134,11 @@ bc-migration: PDFFILE=BouncyCastle-wolfSSL-Migration-Guide.pdf
bc-migration: build
$(Q)$(DOCKER_CMD)

.PHONY: mbedtls-migration
mbedtls-migration: MANPATH=MbedTLS-Migration
mbedtls-migration: PDFFILE=MbedTLS-wolfSSL-Migration-Guide.pdf
mbedtls-migration: build
$(Q)$(DOCKER_CMD)

clean:
$(Q)rm -rf build
23 changes: 23 additions & 0 deletions MbedTLS-Migration/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-include ../common/common.am
.DEFAULT_GOAL := all
all: pdf html


SOURCES = chapter01.md \
chapter02.md \
chapter03.md \
chapter04.md \
chapter05.md \
chapter06.md \
chapter07.md \
chapter08.md \
chapter09.md \
chapter10.md

PDF = MbedTLS-wolfSSL-Migration-Guide.pdf

.PHONY: html-prep
html-prep:

.PHONY: pdf-prep
pdf-prep:
27 changes: 27 additions & 0 deletions MbedTLS-Migration/header.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
% Mbed TLS Migration Guide ![](logo.png)

---
header-includes:
# Blank pages on new sections
- \usepackage{titlesec}
- \newcommand{\sectionbreak}{\clearpage}
# Fancy page headers
- \usepackage{fancyhdr}
- \pagestyle{fancy}
- \fancyfoot[LO,RE]{COPYRIGHT \copyright 2026 wolfSSL Inc.}
# Wrap long syntax highlighting code blocks
- \usepackage{fvextra}
- \DefineVerbatimEnvironment{Highlighting}{Verbatim}{breaklines,commandchars=\\\{\}}
# Wrap long non-syntax highlighted code blocks
- \usepackage{listings}
- \let\verbatim\undefined
- \let\verbatimend\undefined
- \lstnewenvironment{verbatim}{\lstset{breaklines,basicstyle=\ttfamily}}{}
# Allow line breaks after underscores in long identifiers
- \let\oldunderscore\_
- \renewcommand{\_}{\oldunderscore\allowbreak}
# Keep monospace identifiers and options from being hyphenated
monofontoptions:
- HyphenChar=None
subparagraph: yes
---
35 changes: 35 additions & 0 deletions MbedTLS-Migration/mkdocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
site_name: Mbed TLS Migration Guide
site_url: https://wolfssl.com/
docs_dir: build/html/
site_dir: html/
copyright: Copyright © 2026 wolfSSL Inc.
nav:
- "1. Introduction": index.md
- "2. Design Differences": chapter02.md
- "3. Porting a TLS Client": chapter03.md
- "4. Common Pitfalls": chapter04.md
- "5. Hardware and Platforms": chapter05.md
- "6. Features Beyond Mbed TLS": chapter06.md
- "7. Getting Started": chapter07.md
- "8. API Mapping": chapter08.md
- "9. Configuration Equivalents": chapter09.md
- "10. Support": chapter10.md
theme:
name: null
custom_dir: ../mkdocs-material/material
language: en
palette:
primary: indigo
accent: indigo
font:
text: Roboto
code: Roboto Mono
icon: "logo.png"
logo: logo.png
favicon: logo.png
feature:
tabs: true
extra_css: [skin.css]
extra:
generator: false
use_directory_urls: false
55 changes: 55 additions & 0 deletions MbedTLS-Migration/src/chapter01.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Introduction

This guide helps developers move embedded and host applications from Mbed TLS
to wolfSSL. It covers the design differences that matter, a side-by-side TLS
client, the pitfalls seen most often in real migrations, and full API and
configuration mappings.

## Audience

This guide is for C developers and architects who maintain a product built on
Mbed TLS 3.6 or 4.x and are planning or evaluating a move to wolfSSL. It
assumes you know the Mbed TLS API your product uses today.

## Scope

The guide covers both Mbed TLS 3.6 and 4.x, with 4.x as the primary reference.
The TLS-layer calls (`mbedtls_ssl_*`) changed much less between 3.6 and 4.x
than the crypto layer did, so most of the guide applies to both. Where the two
versions differ, the text says so.

The guide focuses on TLS and DTLS. Code that calls Mbed TLS or PSA crypto
functions directly can move to the native wolfCrypt `wc_*` API, or keep the
PSA Crypto API on top of wolfCrypt through wolfPSA (see
[Features Beyond Mbed TLS](chapter06.md)).

## Mbed TLS Support Status

As of October 2026:

| Branch | Status |
| -------------------- | -------------------------------------------------------------------------------- |
| 2.28 LTS | End of life. The last release was 2.28.10 (March 2025). |
| 3.6 LTS | Maintained until March 2027. Latest release 3.6.7 (July 2026). |
| 4.0 | First 4.x release (October 2025), with crypto split out into TF-PSA-Crypto 1.0. |
| 4.1 LTS | Announced with fixes until March 2029. Latest release 4.1.1 (July 2026). |
| 4.2 | Current mainline release (July 2026), with TF-PSA-Crypto 1.2. |

In October 2026 the Mbed TLS team
[announced](https://lists.trustedfirmware.org/archives/list/mbed-tls@lists.trustedfirmware.org/thread/YPIGCB7WN6GCHOGEGKOJXUYAP7OK527W/)
that its current maintainers will stop working on the TLS and X.509 libraries
in April 2027. Unless new maintainers take over, security fixes and releases
for those libraries end then, which also affects the TLS and X.509 part of the
4.1 LTS branch. The crypto layer, TF-PSA-Crypto, stays actively maintained.

## How to Use This Guide

1. Read [Design Differences](chapter02.md) for the concepts that change.
2. Port your TLS setup and I/O code using
[Porting a TLS Client](chapter03.md) and the [API Mapping](chapter08.md).
3. Build wolfSSL with the features your Mbed TLS configuration uses, using
[Configuration Equivalents](chapter09.md) as a checklist.
4. Check your port against [Common Pitfalls](chapter04.md). Most of them
compile fine and fail at runtime.
5. Test against your existing Mbed TLS peers, as described in
[Getting Started](chapter07.md).
63 changes: 63 additions & 0 deletions MbedTLS-Migration/src/chapter02.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Design Differences

This chapter compares how Mbed TLS and wolfSSL are structured, to frame the
porting work in the following chapters.

| Topic | Mbed TLS | wolfSSL |
| ------------------ | ----------------------------------------- | ----------------------------------------- |
| Context model | `mbedtls_ssl_config` plus `mbedtls_ssl_context` | `WOLFSSL_CTX` plus `WOLFSSL` |
| Protocol and role | `mbedtls_ssl_config_defaults()` arguments | The method passed to `wolfSSL_CTX_new()` |
| Crypto API | Legacy (3.6) or PSA (4.x) | Native wolfCrypt `wc_*` API, optional PSA via wolfPSA |
| RNG | Wired up by the application (3.6), internal to PSA (4.x) | Created and seeded internally per session |
| Configuration | `mbedtls_config.h`, `crypto_config.h` | `./configure`, CMake or `user_settings.h` |
| Return codes | 0 on success, negative error | `WOLFSSL_SUCCESS` (1) for TLS calls, 0 for `wc_*` calls |
| Transport | `mbedtls_ssl_set_bio()` callbacks | `wolfSSL_set_fd()` or I/O callbacks |
| Hardware offload | `_ALT` (3.6) or PSA drivers (4.x) | Crypto callbacks plus in-tree ports |
| License | Apache-2.0 or GPL-2.0-or-later | GPLv3 or commercial |

## One Context for Configuration, One per Connection

An `mbedtls_ssl_config` becomes a `WOLFSSL_CTX`, and an `mbedtls_ssl_context`
becomes a `WOLFSSL`. Instead of passing endpoint, transport and preset to
`mbedtls_ssl_config_defaults()`, you pass a method to `wolfSSL_CTX_new()`:
`wolfTLS_client_method()` negotiates the highest TLS version both sides
support, and `wolfDTLSv1_3_client_method()` selects DTLS 1.3. Configure the
`WOLFSSL_CTX` once, share it across threads, and create one `WOLFSSL` per
connection.

## No RNG Plumbing

wolfSSL needs neither the 3.6 CTR-DRBG wiring nor the 4.x `psa_crypto_init()`
call: every `WOLFSSL` seeds its own RNG from the platform entropy source. On
bare metal you provide that source once, through `CUSTOM_RAND_GENERATE_SEED`
or a `wc_GenerateSeed()` port, the counterpart of
`MBEDTLS_ENTROPY_HARDWARE_ALT` or `MBEDTLS_PSA_DRIVER_GET_ENTROPY`, and wolfSSL
runs its Hash_DRBG on top. When you call wolfCrypt directly, functions that
need randomness take an explicit `WC_RNG*`.

## Memory Footprint

Mbed TLS reserves a 16 KB input and a 16 KB output buffer per connection by
default, unless you shrink them with `MBEDTLS_SSL_IN_CONTENT_LEN` and
`MBEDTLS_SSL_OUT_CONTENT_LEN`. wolfSSL allocates each record at its actual size
and frees it once processed, so peak use follows the largest record you see,
still up to 16 KB unless both sides agree on Max Fragment Length. For systems
without `malloc()`, `--enable-staticmemory` serves every allocation from pools
you provide.

Two wolfSSL blog posts measured both libraries:
[TLS 1.3 in 30.8 KB](https://www.wolfssl.com/tls-1-3-in-30-8-kb-wolfssl-vs-mbedtls/)
found wolfSSL 5% to 44% smaller in flash on a Cortex-M33, and
[an apples-to-apples benchmark](https://www.wolfssl.com/wolfssl-vs-mbedtls-an-apples-to-apples-benchmark-across-intel-arm-cortex-a-and-cortex-m-and-risc-v-targets/)
found wolfCrypt's public-key operations several times faster on Intel, ARM and
RISC-V.

## Licensing

Mbed TLS is dual licensed under Apache-2.0 and GPL-2.0-or-later. Apache-2.0 lets
you ship closed-source firmware with only notice requirements. wolfSSL is dual
licensed under GPLv3 and a commercial license, so most closed-source firmware
needs a commercial license. GPLv3 is rarely a way around it: for consumer
devices it also requires that users can install modified software, which
locked-down firmware usually cannot allow. Contact wolfSSL early, so licensing
is settled before engineering starts.
152 changes: 152 additions & 0 deletions MbedTLS-Migration/src/chapter03.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Porting a TLS Client

Both functions below verify the server against a CA file, check its host name,
send a request and read the reply over an already connected socket. They were
compiled against Mbed TLS 4.1 and wolfSSL 5.9.4, and each was run against the
other library's test server.

## Mbed TLS 4.x

```c
#include <psa/crypto.h>
#include <mbedtls/ssl.h>
#include <mbedtls/net_sockets.h>
#include <mbedtls/x509_crt.h>

int tls_get(int fd, const char* host, const char* caFile)
{
int ret;
mbedtls_net_context net;
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_x509_crt ca;
unsigned char buf[256];
const char req[] = "GET / HTTP/1.0\r\n\r\n";

mbedtls_net_init(&net);
net.fd = fd;
mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);
mbedtls_x509_crt_init(&ca);

ret = (int)psa_crypto_init(); /* 4.x: before any parsing */
if (ret != PSA_SUCCESS)
goto out;
if ((ret = mbedtls_x509_crt_parse_file(&ca, caFile)) != 0)
goto out;
if ((ret = mbedtls_ssl_config_defaults(&conf, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0)
goto out;
mbedtls_ssl_conf_ca_chain(&conf, &ca, NULL);
/* 3.6 only: mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &drbg); */

if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0 ||
(ret = mbedtls_ssl_set_hostname(&ssl, host)) != 0)
goto out;
mbedtls_ssl_set_bio(&ssl, &net, mbedtls_net_send, mbedtls_net_recv, NULL);

while ((ret = mbedtls_ssl_handshake(&ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
ret != MBEDTLS_ERR_SSL_WANT_WRITE)
goto out;
}
ret = mbedtls_ssl_write(&ssl, (const unsigned char*)req, sizeof(req) - 1);
if (ret <= 0)
goto out;
do {
ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf) - 1);
} while (ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET);
if (ret > 0)
ret = 0;
mbedtls_ssl_close_notify(&ssl);

out:
mbedtls_ssl_free(&ssl);
mbedtls_ssl_config_free(&conf);
mbedtls_x509_crt_free(&ca);
return ret;
}
```

## wolfSSL

```c
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <string.h>

int tls_get(int fd, const char* host, const char* caFile)
{
int ret = -1;
WOLFSSL_CTX* ctx = NULL;
WOLFSSL* ssl = NULL;
char buf[256];
const char req[] = "GET / HTTP/1.0\r\n\r\n";

ctx = wolfSSL_CTX_new(wolfTLS_client_method()); /* TLS 1.2 or 1.3 */
if (ctx == NULL)
goto out;
if (wolfSSL_CTX_load_verify_locations(ctx, caFile, NULL)
!= WOLFSSL_SUCCESS)
goto out;
wolfSSL_CTX_set_verify(ctx, WOLFSSL_VERIFY_PEER, NULL);

ssl = wolfSSL_new(ctx);
if (ssl == NULL)
goto out;
if (wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, host,
(unsigned short)strlen(host)) != WOLFSSL_SUCCESS ||
wolfSSL_check_domain_name(ssl, host) != WOLFSSL_SUCCESS ||
wolfSSL_set_fd(ssl, fd) != WOLFSSL_SUCCESS)
goto out;

ret = wolfSSL_connect(ssl);
if (ret != WOLFSSL_SUCCESS) {
ret = wolfSSL_get_error(ssl, ret);
goto out;
}
ret = wolfSSL_write(ssl, req, (int)sizeof(req) - 1);
if (ret <= 0) {
ret = wolfSSL_get_error(ssl, ret);
goto out;
}
ret = wolfSSL_read(ssl, buf, (int)sizeof(buf) - 1);
if (ret <= 0) {
ret = wolfSSL_get_error(ssl, ret);
goto out;
}
ret = 0;
wolfSSL_shutdown(ssl);

out:
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
return ret;
}
```

## What Changed

- The RNG setup and `psa_crypto_init()` are gone.
- `wolfSSL_CTX_load_verify_locations()` parses and installs the CA in one call,
and `wolfSSL_CTX_set_verify()` makes peer verification explicit (see
"Peer Verification Is On, and Should Stay On" in
[Common Pitfalls](chapter04.md)).
- `wolfSSL_connect()` drives the whole handshake on a blocking socket. With a
non-blocking one, call it again while `wolfSSL_get_error()` returns
`WOLFSSL_ERROR_WANT_READ` or `WOLFSSL_ERROR_WANT_WRITE`.
- `wolfSSL_UseSNI()` needs SNI support, which `./configure` enables by
default only on x86 and aarch64 hosts. On embedded targets, build with
`--enable-sni` or define `HAVE_SNI`.
- The read loop is gone too. Since 4.0, `mbedtls_ssl_read()` returns
`MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET` whenever a TLS 1.3 server sends
a ticket, while wolfSSL handles tickets internally.

On failure the wolfSSL version returns a `wolfSSL_get_error()` code, and some
of those are positive, such as `WOLFSSL_ERROR_ZERO_RETURN` when the peer closed
the connection. Check the result with `!= 0` rather than `< 0`.

Call `wolfSSL_Init()` once at startup and `wolfSSL_Cleanup()` once at shutdown,
not around each connection, because `wolfSSL_Cleanup()` also frees the session
cache.
Loading
Loading