Skip to content
Draft
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
35 changes: 35 additions & 0 deletions .github/workflows/mbedtls-compat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Mbed TLS compatibility

on:
push:
pull_request:

jobs:
# make MBEDTLS_COMPAT=1 mbedtls-compat-test: the Mbed TLS 4.x API on
# wolfSSL, tested with Mbed TLS's sample programs and test data. See the
# README.
mbedtls-compat:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
wolfssl-ref: [ master, v5.9.4-stable ]
options: [ "", "MBEDTLS_THREADING_C=1 ASAN=1" ]

steps:
- name: Check out wolfPSA
uses: actions/checkout@v4

- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential autoconf automake libtool libtool-bin pkg-config python3-jinja2 faketime

- name: Clone sibling wolfSSL
run: git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} https://github.com/wolfSSL/wolfssl ../wolfssl

- name: Clone sibling Mbed TLS
run: git clone --depth 1 --branch mbedtls-4.2.0 --recurse-submodules --shallow-submodules https://github.com/Mbed-TLS/mbedtls ../mbedtls

- name: Build and run the compatibility tests
run: make MBEDTLS_COMPAT=1 ${{ matrix.options }} mbedtls-compat-test
58 changes: 51 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,31 @@ WOLFCRYPT_SRC := \
WOLFCRYPT_OBJ := $(patsubst $(WOLFSSL_PATH)/wolfcrypt/src/%.c,$(OBJDIR)/wolfcrypt_%.o,$(WOLFCRYPT_SRC))
WOLFCRYPT_OBJ_PIC := $(patsubst $(WOLFSSL_PATH)/wolfcrypt/src/%.c,$(OBJDIR_PIC)/wolfcrypt_%.o,$(WOLFCRYPT_SRC))

# MBEDTLS_COMPAT=1 adds the Mbed TLS 4.x TLS, X.509 and network API, on
# wolfSSL's TLS compiled from WOLFSSL_PATH like wolfCrypt.
# See src/mbedtls_compat.c and src/mbedtls_net.c.
WOLFSSL_TLS_SRC :=
ifeq ($(MBEDTLS_COMPAT),1)
CPPFLAGS += -DWOLFPSA_MBEDTLS_COMPAT
ENROLLMENT_ALG := 1
WOLFSSL_TLS_SRC := $(addprefix $(WOLFSSL_PATH)/src/,internal.c keys.c ssl.c \
tls.c tls13.c wolfio.c)
endif
WOLFSSL_TLS_OBJ := $(patsubst $(WOLFSSL_PATH)/src/%.c,$(OBJDIR)/wolfssl_%.o, \
$(WOLFSSL_TLS_SRC))
WOLFSSL_TLS_OBJ_PIC := $(patsubst $(WOLFSSL_PATH)/src/%.c, \
$(OBJDIR_PIC)/wolfssl_%.o,$(WOLFSSL_TLS_SRC))

# MBEDTLS_THREADING_C=1 builds a thread safe library, as in Mbed TLS. See
# user_settings.h. Applications define MBEDTLS_THREADING_C too.
ifeq ($(MBEDTLS_THREADING_C),1)
CPPFLAGS += -DMBEDTLS_THREADING_C
LDFLAGS += -pthread
endif

# Changing MBEDTLS_THREADING_C rebuilds every object
# Changing MBEDTLS_COMPAT or MBEDTLS_THREADING_C rebuilds every object
OPTIONS_STAMP := $(BUILD_DIR)/options_$(if \
$(filter 1,$(MBEDTLS_COMPAT)),compat,nocompat)_$(if \
$(filter 1,$(MBEDTLS_THREADING_C)),threads,nothreads)

WOLFSSL_CPPFLAGS ?= -DWOLFSSL_USER_SETTINGS
Expand Down Expand Up @@ -135,7 +151,7 @@ LDFLAGS += --coverage
endif

.PHONY: all clean psa-objects unit-run run-tests cov covclean \
want-surface-test
want-surface-test mbedtls-compat-test

all: $(LIBNAME) $(SHLIBNAME)

Expand All @@ -145,11 +161,13 @@ all: $(LIBNAME) $(SHLIBNAME)
# bundled wolfCrypt sources are out of scope.
psa-objects: $(OBJ)

$(LIBNAME): $(OBJ) $(WOLFCRYPT_OBJ)
$(LIBNAME): $(OBJ) $(WOLFCRYPT_OBJ) $(WOLFSSL_TLS_OBJ)
rm -f $@
$(AR) rcs $@ $^
$(RANLIB) $@

$(SHLIBNAME): $(OBJ_PIC) $(WOLFCRYPT_OBJ_PIC) $(EXPORT_MAP)
$(SHLIBNAME): $(OBJ_PIC) $(WOLFCRYPT_OBJ_PIC) $(WOLFSSL_TLS_OBJ_PIC) \
$(EXPORT_MAP)
$(CC) -shared -Wl,--version-script,$(EXPORT_MAP) -Wl,-Bsymbolic-functions -o $@ $(filter-out $(EXPORT_MAP),$^) $(LDFLAGS)

# Build the PSA_WANT_* macros based on what the wolfCrypt build supports.
Expand All @@ -172,7 +190,8 @@ want-surface-test: $(LIBNAME) $(SHLIBNAME)
-lpthread -lm
$(BUILD_DIR)/psa_want_surface_test

$(OBJ) $(WOLFCRYPT_OBJ) $(OBJ_PIC) $(WOLFCRYPT_OBJ_PIC): $(OPTIONS_STAMP)
$(OBJ) $(WOLFCRYPT_OBJ) $(WOLFSSL_TLS_OBJ) $(OBJ_PIC) $(WOLFCRYPT_OBJ_PIC) \
$(WOLFSSL_TLS_OBJ_PIC): $(OPTIONS_STAMP)

$(OPTIONS_STAMP):
@mkdir -p $(@D)
Expand All @@ -195,6 +214,14 @@ $(OBJDIR_PIC)/wolfcrypt_%.o: $(WOLFSSL_PATH)/wolfcrypt/src/%.c
@mkdir -p $(OBJDIR_PIC)
$(CC) $(CPPFLAGS) $(DEPFLAGS) $(CFLAGS) -fPIC -c $< -o $@

$(OBJDIR)/wolfssl_%.o: $(WOLFSSL_PATH)/src/%.c
@mkdir -p $(OBJDIR)
$(CC) $(CPPFLAGS) $(DEPFLAGS) $(CFLAGS) -c $< -o $@

$(OBJDIR_PIC)/wolfssl_%.o: $(WOLFSSL_PATH)/src/%.c
@mkdir -p $(OBJDIR_PIC)
$(CC) $(CPPFLAGS) $(DEPFLAGS) $(CFLAGS) -fPIC -c $< -o $@

# The unit tests. The servers (psa_tls_client, psa_tls_server) and the
# benchmark are not unit tests and are not listed.
UNIT_TESTS := psa_api_test \
Expand Down Expand Up @@ -277,6 +304,22 @@ unit-run: all
@$(MAKE) -C test $(UNIT_TESTS)
@$(MAKE) run-tests

# Mbed TLS repo for running their example programs.
MBEDTLS_PATH ?= ../mbedtls

ifeq ($(MBEDTLS_COMPAT),1)
mbedtls-compat-test: $(LIBNAME)
$(MAKE) -C test/mbedtls_compat WOLFPSA_PATH=$(CURDIR) \
WOLFSSL_PATH=$(abspath $(WOLFSSL_PATH)) \
MBEDTLS_PATH=$(abspath $(MBEDTLS_PATH)) \
BUILD_DIR=$(abspath $(BUILD_DIR))/mbedtls_compat \
PSA_CONFIG_DIR=$(abspath $(dir $(PSA_CONFIG))) \
WOLFPSA_LDFLAGS="$(LDFLAGS)" check
else
mbedtls-compat-test:
@echo "Run make MBEDTLS_COMPAT=1 mbedtls-compat-test"; exit 1
endif

# Build everything with gcov instrumentation, run the unit tests, and emit an
# HTML coverage report for src/*.c (the bundled wolfCrypt sources are
# excluded by the -f filter). The instrumented objects are dropped once the
Expand Down Expand Up @@ -326,5 +369,6 @@ covclean:
clean:
rm -rf $(BUILD_DIR) $(LIBNAME) $(SHLIBNAME)

-include $(OBJ:.o=.d) $(WOLFCRYPT_OBJ:.o=.d)
-include $(OBJ_PIC:.o=.d) $(WOLFCRYPT_OBJ_PIC:.o=.d)
-include $(OBJ:.o=.d) $(WOLFCRYPT_OBJ:.o=.d) $(WOLFSSL_TLS_OBJ:.o=.d)
-include $(OBJ_PIC:.o=.d) $(WOLFCRYPT_OBJ_PIC:.o=.d) \
$(WOLFSSL_TLS_OBJ_PIC:.o=.d)
111 changes: 111 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,117 @@ default), using python3. Set `PYTHON` to use another interpreter. Code that
includes `<psa/crypto.h>` needs that directory on its include path next to
`wolfpsa/`, and builds only after `make` has generated the header.

## Mbed TLS compatibility

`MBEDTLS_COMPAT=1` adds the Mbed TLS 4.x TLS, X.509 and network API on top of
wolfSSL, so that applications written for Mbed TLS 4.x build against wolfPSA
without source changes. wolfPSA already provides the TF-PSA-Crypto side
(`psa_*`, `mbedtls_psa_crypto_free`, `mbedtls/platform.h`).

```
make MBEDTLS_COMPAT=1
```

- wolfSSL's TLS sources are compiled from `WOLFSSL_PATH` into
`libwolfpsa.a`, like wolfCrypt, with the TLS settings in
`user_settings.h`. Changing the option rebuilds everything.
- It also turns on `ENROLLMENT_ALG=1` (`WOLFPSA_ENROLLMENT_ALG`): a
volatile key may have the Mbed TLS enrollment algorithm, a second
permitted algorithm.
- Applications use `-I<wolfPSA>/wolfpsa` and `-I<wolfPSA>/build/include`, and
link `libwolfpsa.a`, in place of Mbed TLS's libraries. The headers include
no wolfSSL header.
- The functions have the Mbed TLS names, so code that finds them by symbol
name (`dlsym`, language bindings) works too.
- With `MBEDTLS_THREADING_C=1`, TLS is thread safe too.
- TLS uses wolfPSA's constant-time AES. `AES_FAST=1` applies to TLS too.
- As in Mbed TLS, certificates may have NULL parameters after an ECDSA
signature OID (`WOLFSSL_ECC_SIGALG_PARAMS_NULL_ALLOWED`).

Mapped so far:

- net: init, connect, bind, accept, close, free, recv, send, usleep
- x509_crt: init, free, parse, parse_der, parse_file
- ssl_config: init, free, defaults; ssl_conf: authmode, ca_chain, own_cert,
verify, dbg
- ssl: init, free, setup, session_reset, set_bio, set_verify, set_hostname,
handshake, get_verify_result, get_ciphersuite, read, write, close_notify
- pk, with or without the option: init, free, parse_key, parse_keyfile
- threading, with `MBEDTLS_THREADING_C=1`, with or without the option: mutex
init, free, lock, unlock; condition_variable init, free, signal, broadcast,
wait

Known differences from Mbed TLS:

- A wolfSSL error with no Mbed TLS match returns a general code for the
call, such as `MBEDTLS_ERR_SSL_INTERNAL_ERROR`. wolfSSL's own codes are not
returned: some Mbed TLS 4.x codes are PSA values (-128 to -160), which
overlap wolfCrypt codes.
- `conf_*` calls after `mbedtls_ssl_setup` take effect at the next
`mbedtls_ssl_session_reset`.
- A second `mbedtls_ssl_conf_own_cert` replaces the first. Mbed TLS keeps
both and picks one for each handshake.
- `mbedtls_ssl_get_ciphersuite` returns IANA names, such as
`TLS_AES_128_GCM_SHA256`, where Mbed TLS has `TLS1-3-AES-128-GCM-SHA256`.
- `mbedtls_net_accept` is TCP only.
- `mbedtls_pk_context` has the key's DER and nothing else. Encrypted keys
and EC keys with a compressed public key are not supported.
- `mbedtls_x509_crt` has one node per certificate, with no parsed fields
apart from `raw`. Parse errors are `MBEDTLS_ERR_X509_INVALID_FORMAT` or
`MBEDTLS_ERR_X509_ALLOC_FAILED`.
- wolfSSL parses some certificates differently. For example, it rejects
serial number 0 and a pathlen above `WOLFSSL_MAX_PATH_LEN`, and accepts
spaces in PEM.
- `mbedtls_ssl_conf_ca_chain` leaves out a CA that wolfSSL does not load,
such as an expired one. A peer certificate from that CA then fails as
`NOT_TRUSTED`, where Mbed TLS reports `EXPIRED`.
- An IP address host name is matched against the certificate's IP address
SANs only. Mbed TLS also matches a DNS SAN with the same text, or the CN
of a certificate without SANs.
- `mbedtls_ssl_set_hostname(ssl, NULL)` after `mbedtls_ssl_setup` starts a
new connection, so call it before the handshake.
- With `VERIFY_OPTIONAL`, a resumed session reports
`MBEDTLS_X509_BADCERT_MISSING`, where Mbed TLS keeps the result of the
first handshake.
- The verify flags come from wolfSSL, which reports one failure from each
certificate's signature and date checks, and one from the later checks of
the peer's own, such as the host name. So the verify callback and
`mbedtls_ssl_get_verify_result` can report fewer flags than Mbed TLS: an
untrusted certificate that has also expired is only `NOT_TRUSTED`.
- The verify callback gets the certificates the peer sent, in that order,
then above them the CA from the CA list that issued the last one. Mbed TLS
builds the chain itself, and leaves out a certificate the peer sent once
it reaches a CA from the list.
- wolfSSL keeps the intermediate CAs of a chain that verifies, for the
config's later handshakes. A peer that then leaves them out still
verifies, where Mbed TLS reports `NOT_TRUSTED`.
- `mbedtls_ssl_conf_dbg` is accepted and ignored.
- No global mutexes such as `mbedtls_threading_gmtime_mutex`.

Tests build the Mbed TLS migration guide client
(`test/mbedtls_compat/client-tls-mbedtls.c`) and Mbed TLS's sample
programs, unchanged, against `libwolfpsa.a`, and run them against the
wolfSSL example server. Mbed TLS's server programs run against the wolfSSL
example client and `ssl_client1`. `test/mbedtls_compat/x509_crt_test.c` and
`pk_test.c` parse Mbed TLS's test certificates and keys. With
`MBEDTLS_THREADING_C=1`, `ssl_pthread_server` serves clients at the same
time. To run them:

```
make MBEDTLS_COMPAT=1 mbedtls-compat-test
```

- `MBEDTLS_PATH` (default `../mbedtls`) is an Mbed TLS 4.x checkout with
its submodules. Nothing from Mbed TLS is copied or linked, apart from its
test certificate data.
- The example server and client come from a wolfSSL configured out of tree
from `WOLFSSL_PATH`, which must not be configured in place. The tests use
the certificates in its `certs/`.
- Also needs python3 with jinja2. Without faketime, the case that needs it
is skipped.
- `WRAP` prefixes each test program, for example `WRAP=valgrind`. Server
programs are stopped by the script, so their exit codes are not checked.

## Testing (PSA Architecture Test Suite)

See `test-psa-api/README.md` for instructions on how to run the tests.
Expand Down
Loading
Loading