diff --git a/.github/workflows/mbedtls-compat.yml b/.github/workflows/mbedtls-compat.yml new file mode 100644 index 00000000..491e296a --- /dev/null +++ b/.github/workflows/mbedtls-compat.yml @@ -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 diff --git a/Makefile b/Makefile index 5a408f9d..f0d15373 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,21 @@ 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) @@ -86,8 +101,9 @@ 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 @@ -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) @@ -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. @@ -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) @@ -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 \ @@ -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 @@ -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) diff --git a/README.md b/README.md index adbb0ab5..156f7d97 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,117 @@ default), using python3. Set `PYTHON` to use another interpreter. Code that includes `` 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` and `-I/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. diff --git a/src/mbedtls_compat.c b/src/mbedtls_compat.c new file mode 100644 index 00000000..4008035b --- /dev/null +++ b/src/mbedtls_compat.c @@ -0,0 +1,1502 @@ +/* mbedtls_compat.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Mbed TLS 4.x TLS, X.509 and network API on wolfSSL (MBEDTLS_COMPAT=1). */ + +#ifdef HAVE_CONFIG_H + #include +#endif /* HAVE_CONFIG_H */ + +#include "psa_config.h" + +#if defined(WOLFPSA_MBEDTLS_COMPAT) + +#include +#include +#include +#include +#include +#include +#include +#include "mbedtls_compat_internal.h" + +#if defined(WOLFCRYPT_ONLY) || !defined(HAVE_SNI) + #error "wolfPSA: MBEDTLS_COMPAT needs wolfSSL TLS with SNI" +#endif /* WOLFCRYPT_ONLY || !HAVE_SNI */ +/* MbedVerifyCb() needs the call for the peer's certificate when it passes */ +#ifndef WOLFSSL_ALWAYS_VERIFY_CB + #error "wolfPSA: MBEDTLS_COMPAT needs WOLFSSL_ALWAYS_VERIFY_CB" +#endif /* !WOLFSSL_ALWAYS_VERIFY_CB */ + +/* Unknown codes map to the last entry */ +int MbedIoErr(int code, int dir, int toWolf) +{ + static const struct { + int wolf; + int mbed[2]; + } map[] = { + { WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ), + { MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_ERR_SSL_WANT_WRITE } }, + { WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_RST), + { MBEDTLS_ERR_NET_CONN_RESET, MBEDTLS_ERR_NET_CONN_RESET } }, + /* A receive of 0 bytes is EOF */ + { WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_CLOSE), + { 0, MBEDTLS_ERR_NET_CONN_RESET } }, + { WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_TIMEOUT), + { MBEDTLS_ERR_SSL_TIMEOUT, MBEDTLS_ERR_SSL_TIMEOUT } }, + { WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_ISR), + { MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_ERR_SSL_WANT_WRITE } }, + { WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_GENERAL), + { MBEDTLS_ERR_NET_RECV_FAILED, MBEDTLS_ERR_NET_SEND_FAILED } } + }; + const size_t last = XELEM_CNT(map) - 1; + size_t i; + + if (toWolf) { + /* Translate Mbed TLS error to wolfSSL error */ + for (i = 0; i < last; i++) { + if (map[i].mbed[dir] == code) { + break; + } + } + return map[i].wolf; + } + + /* wolfSSL error to Mbed TLS */ + for (i = 0; i < last; i++) { + if (map[i].wolf == code) { + break; + } + } + return map[i].mbed[dir]; +} + +/* Mbed TLS checks these of the peer's certificate after the verify callback, + * which does not see them */ +#define MBED_USAGE_FLAGS \ + (MBEDTLS_X509_BADCERT_KEY_USAGE | MBEDTLS_X509_BADCERT_EXT_KEY_USAGE) + +static void MbedChainDropCAs(mbedtls_ssl_context* ssl); + +/* Map the wolfSSL error behind a failed call to an Mbed TLS return code */ +static int MbedSslErr(mbedtls_ssl_context* ssl, int ret) +{ + static const struct { + int wolf; + int mbed; + } map[] = { + /* A zero-length read or write */ + { WOLFSSL_ERROR_NONE, 0 }, + { WOLFSSL_ERROR_WANT_READ, MBEDTLS_ERR_SSL_WANT_READ }, + { WOLFSSL_ERROR_WANT_WRITE, MBEDTLS_ERR_SSL_WANT_WRITE }, + { WOLFSSL_ERROR_ZERO_RETURN, MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY }, + { WC_NO_ERR_TRACE(SOCKET_PEER_CLOSED_E), MBEDTLS_ERR_SSL_CONN_EOF }, + { WC_NO_ERR_TRACE(NO_PEER_CERT), MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE } + }; + int err = wolfSSL_get_error(ssl->ssl, ret); + size_t i; + + /* The handshake may have ended before the verify callback ran */ + MbedChainDropCAs(ssl); + if (ssl->verify_ret != 0) { + return ssl->verify_ret; + } + /* A failed verify with VERIFY_REQUIRED is what ended the handshake */ + if (ssl->verify_flags != 0 && + ssl->authmode == MBEDTLS_SSL_VERIFY_REQUIRED) { + return ((ssl->verify_flags & ~MBED_USAGE_FLAGS) != 0) ? + MBEDTLS_ERR_X509_CERT_VERIFY_FAILED : + MBEDTLS_ERR_SSL_BAD_CERTIFICATE; + } + for (i = 0; i < XELEM_CNT(map); i++) { + if (map[i].wolf == err) { + return map[i].mbed; + } + } + /* wolfSSL codes overlap Mbed TLS and PSA codes */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; +} + +/* WOLFSSL_SUCCESS becomes 0. wolfSSL codes overlap Mbed TLS and PSA codes, + * so other failures become fallback. */ +static int MbedRet(int ret, int fallback) +{ + if (ret == WOLFSSL_SUCCESS) { + return 0; + } + return (ret == WC_NO_ERR_TRACE(MEMORY_E)) ? MBEDTLS_ERR_SSL_ALLOC_FAILED : + fallback; +} + +static void MbedConfErr(mbedtls_ssl_config* conf, int err) +{ + if (conf->err == 0) { + conf->err = err; + } +} + +static int MbedRecvCb(WOLFSSL* ssl, char* buf, int sz, void* ctx) +{ + mbedtls_ssl_context* s = (mbedtls_ssl_context*)ctx; + int ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + + (void)ssl; + if (s->f_recv_timeout != NULL) { + ret = s->f_recv_timeout(s->p_bio, (unsigned char*)buf, (size_t)sz, 0); + } + else if (s->f_recv != NULL) { + ret = s->f_recv(s->p_bio, (unsigned char*)buf, (size_t)sz); + } + if (ret > 0) { + return ret; + } + return MbedIoErr(ret, MBED_IO_RECV, 1); +} + +static int MbedSendCb(WOLFSSL* ssl, char* buf, int sz, void* ctx) +{ + mbedtls_ssl_context* s = (mbedtls_ssl_context*)ctx; + int ret = MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + + (void)ssl; + if (s->f_send != NULL) { + ret = s->f_send(s->p_bio, (const unsigned char*)buf, (size_t)sz); + } + if (ret > 0) { + return ret; + } + return MbedIoErr(ret, MBED_IO_SEND, 1); +} + +/* Unknown codes map to the last entry */ +static uint32_t MbedVerifyFlag(int err) +{ + static const struct { + int wolf; + uint32_t mbed; + } map[] = { + { WC_NO_ERR_TRACE(ASN_AFTER_DATE_E), MBEDTLS_X509_BADCERT_EXPIRED }, + { WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E), MBEDTLS_X509_BADCERT_FUTURE }, + { WC_NO_ERR_TRACE(ASN_NO_SIGNER_E), MBEDTLS_X509_BADCERT_NOT_TRUSTED }, + { WC_NO_ERR_TRACE(ASN_SIG_CONFIRM_E), + MBEDTLS_X509_BADCERT_NOT_TRUSTED }, + { WC_NO_ERR_TRACE(DOMAIN_NAME_MISMATCH), + MBEDTLS_X509_BADCERT_CN_MISMATCH }, + { WC_NO_ERR_TRACE(IPADDR_MISMATCH), MBEDTLS_X509_BADCERT_CN_MISMATCH }, + { WC_NO_ERR_TRACE(CRL_CERT_REVOKED), MBEDTLS_X509_BADCERT_REVOKED }, + { WC_NO_ERR_TRACE(OCSP_CERT_REVOKED), MBEDTLS_X509_BADCERT_REVOKED }, + { WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR), MBEDTLS_X509_BADCRL_EXPIRED }, + { WC_NO_ERR_TRACE(KEYUSE_SIGNATURE_E), + MBEDTLS_X509_BADCERT_KEY_USAGE }, + { WC_NO_ERR_TRACE(KEYUSE_ENCIPHER_E), MBEDTLS_X509_BADCERT_KEY_USAGE }, + { WC_NO_ERR_TRACE(EXTKEYUSE_AUTH_E), + MBEDTLS_X509_BADCERT_EXT_KEY_USAGE }, + { WC_NO_ERR_TRACE(RSA_KEY_SIZE_E), MBEDTLS_X509_BADCERT_BAD_KEY }, + { WC_NO_ERR_TRACE(ECC_KEY_SIZE_E), MBEDTLS_X509_BADCERT_BAD_KEY }, + { 0, MBEDTLS_X509_BADCERT_OTHER } + }; + const size_t last = XELEM_CNT(map) - 1; + size_t i; + + for (i = 0; i < last; i++) { + if (map[i].wolf == err) { + break; + } + } + return map[i].mbed; +} + +/* Decodes der without verifying it. Free cert with wc_FreeDecodedCert() + * whatever the result. */ +static int MbedCertDecode(DecodedCert* cert, const byte* der, word32 len) +{ + wc_InitDecodedCert(cert, der, len, NULL); + return wc_ParseCert(cert, CERT_TYPE, NO_VERIFY, NULL); +} + +/* The ID wolfSSL's CA cache finds a CA by */ +static const byte* MbedCertId(const DecodedCert* cert) +{ +#ifndef NO_SKID + return cert->extSubjKeyId; +#else + return cert->subjectHash; +#endif /* !NO_SKID */ +} + +/* The peer's chain while wolfSSL checks it */ +struct MbedChain { + uint32_t* flags; /* MBEDTLS_X509_BADCERT_* of each certificate */ + byte* caIds; /* KEYID_SIZE each: the chain's CAs, to drop from the cache */ + int count; + int caCount; +}; + +static int MbedChainNew(mbedtls_ssl_context* ssl, int count) +{ + size_t sz = sizeof(*ssl->chain) + (size_t)count * sizeof(uint32_t); + + ssl->chain = (struct MbedChain*)XMALLOC(sz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (ssl->chain == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + XMEMSET(ssl->chain, 0, sz); + ssl->chain->flags = (uint32_t*)(ssl->chain + 1); + ssl->chain->count = count; + return 0; +} + +/* wolfSSL caches each CA of a chain it accepts, also one accepted despite an + * error. Notes their IDs so that MbedChainDropCAs() removes them. */ +static int MbedChainNoteCAs(struct MbedChain* chain, + const WOLFSSL_X509_STORE_CTX* store) +{ + DecodedCert* cert; + int i; + + if (chain->caIds != NULL) { + return 0; + } + chain->caIds = (byte*)XMALLOC((size_t)(chain->count - 1) * KEYID_SIZE, + NULL, DYNAMIC_TYPE_TMP_BUFFER); + cert = (DecodedCert*)XMALLOC(sizeof(*cert), NULL, DYNAMIC_TYPE_DCERT); + for (i = 1; chain->caIds != NULL && cert != NULL && i < chain->count; + i++) { + if (MbedCertDecode(cert, store->certs[i].buffer, + store->certs[i].length) == 0) { + XMEMCPY(chain->caIds + chain->caCount * KEYID_SIZE, + MbedCertId(cert), KEYID_SIZE); + chain->caCount++; + } + wc_FreeDecodedCert(cert); + } + XFREE(cert, NULL, DYNAMIC_TYPE_DCERT); + return (chain->caIds != NULL && cert != NULL) ? 0 : + MBEDTLS_ERR_X509_ALLOC_FAILED; +} + +static void MbedChainDropCAs(mbedtls_ssl_context* ssl) +{ + int i; + + if (ssl->chain == NULL || ssl->ssl == NULL) { + return; + } + for (i = 0; i < ssl->chain->caCount; i++) { + (void)RemoveCA(SSL_CM(ssl->ssl), ssl->chain->caIds + i * KEYID_SIZE, + WOLFSSL_CHAIN_CA); + } + ssl->chain->caCount = 0; +} + +/* Call before wolfSSL_free(): MbedChainDropCAs() needs the WOLFSSL */ +static void MbedChainFree(mbedtls_ssl_context* ssl) +{ + if (ssl->chain != NULL) { + MbedChainDropCAs(ssl); + XFREE(ssl->chain->caIds, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(ssl->chain, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ssl->chain = NULL; + } +} + +/* Sets *ca to the CA of ca_chain that issued top, the last certificate the + * peer sent, matched as wolfSSL finds a signer. NULL when top is one of + * them. */ +static int MbedChainCA(mbedtls_x509_crt* ca_chain, + const WOLFSSL_BUFFER_INFO* top, mbedtls_x509_crt** ca) +{ + DecodedCert* cert; + DecodedCert* issuer; + mbedtls_x509_crt* crt; + int ret = 0; + + *ca = NULL; + for (crt = ca_chain; crt != NULL; crt = crt->next) { + if (crt->raw.len == top->length && + XMEMCMP(crt->raw.p, top->buffer, top->length) == 0) { + return 0; + } + } + + cert = (DecodedCert*)XMALLOC(sizeof(*cert), NULL, DYNAMIC_TYPE_DCERT); + issuer = (DecodedCert*)XMALLOC(sizeof(*issuer), NULL, DYNAMIC_TYPE_DCERT); + if (cert == NULL || issuer == NULL) { + ret = MBEDTLS_ERR_X509_ALLOC_FAILED; + } + if (ret == 0 && MbedCertDecode(cert, top->buffer, top->length) == 0) { + for (crt = ca_chain; *ca == NULL && crt != NULL; crt = crt->next) { + if (MbedCertDecode(issuer, crt->raw.p, (word32)crt->raw.len) == 0 && + XMEMCMP(issuer->subjectHash, cert->issuerHash, + KEYID_SIZE) == 0 && + (!cert->extAuthKeyIdSet || + XMEMCMP(issuer->extSubjKeyId, cert->extAuthKeyId, + KEYID_SIZE) == 0)) { + *ca = crt; + } + wc_FreeDecodedCert(issuer); + } + } + if (ret == 0) { + wc_FreeDecodedCert(cert); + } + XFREE(issuer, NULL, DYNAMIC_TYPE_DCERT); + XFREE(cert, NULL, DYNAMIC_TYPE_DCERT); + return ret; +} + +/* As Mbed TLS does once it has checked the chain: calls the verify callback + * for each certificate from the top down, with its flags and the certificates + * the peer sent after it, the CA from the CA list first */ +static int MbedVerifyChain(mbedtls_ssl_context* ssl, + const WOLFSSL_X509_STORE_CTX* store) +{ + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*) = ssl->f_vrfy; + void* p_vrfy = ssl->p_vrfy; + const struct MbedChain* chain = ssl->chain; + const int top = chain->count - 1; + mbedtls_x509_crt peer; + mbedtls_x509_crt* ca = NULL; + mbedtls_x509_crt* crt; + uint32_t flags = 0; + uint32_t usage; + int depth; + int i; + int ret = 0; + + if (f_vrfy == NULL) { + f_vrfy = ssl->conf->f_vrfy; + p_vrfy = ssl->conf->p_vrfy; + } + mbedtls_x509_crt_init(&peer); + for (i = 0; f_vrfy != NULL && ret == 0 && i <= top; i++) { + ret = mbedtls_x509_crt_parse_der(&peer, store->certs[i].buffer, + store->certs[i].length); + } + if (f_vrfy != NULL && ret == 0 && + (chain->flags[top] & MBEDTLS_X509_BADCERT_NOT_TRUSTED) == 0) { + ret = MbedChainCA(ssl->conf->ca_chain, &store->certs[top], &ca); + } + if (ret == 0 && ca != NULL) { + ret = f_vrfy(p_vrfy, ca, top + 1, &flags); + } + ssl->verify_flags = flags; + + for (depth = top; ret == 0 && depth >= 0; depth--) { + usage = (depth == 0) ? (chain->flags[0] & MBED_USAGE_FLAGS) : 0; + flags = chain->flags[depth] & ~usage; + if (f_vrfy != NULL) { + for (crt = &peer, i = 0; i < depth; i++) { + crt = crt->next; + } + ret = f_vrfy(p_vrfy, crt, depth, &flags); + } + ssl->verify_flags |= flags | usage; + } + mbedtls_x509_crt_free(&peer); + + /* As in Mbed TLS, so it does not read as a failed verify */ + return (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) ? + MBEDTLS_ERR_X509_FATAL_ERROR : ret; +} + +/* wolfSSL calls this for each failed check of the peer's chain, top down, + * and last for the peer's certificate once all of its checks are done. Until + * then the handshake goes on, so that the callback and the verdict see the + * flags of the whole chain, as in Mbed TLS. */ +static int MbedVerifyCb(int preverify, WOLFSSL_X509_STORE_CTX* store) +{ + mbedtls_ssl_context* s = (mbedtls_ssl_context*)store->userCtx; + const int depth = store->error_depth; + int ret = 0; + + if (store->totalCerts <= 0) { + if (!preverify) { + s->verify_flags |= MbedVerifyFlag(store->error); + } + return preverify || s->authmode == MBEDTLS_SSL_VERIFY_OPTIONAL; + } + s->peer_cert = 1; + if (s->chain == NULL) { + ret = MbedChainNew(s, store->totalCerts); + } + if (ret == 0 && (s->chain->count != store->totalCerts || depth < 0 || + depth >= s->chain->count)) { + ret = MBEDTLS_ERR_X509_FATAL_ERROR; + } + if (ret == 0 && !preverify) { + s->chain->flags[depth] |= MbedVerifyFlag(store->error); + if (depth > 0) { + ret = MbedChainNoteCAs(s->chain, store); + } + } + if (ret == 0 && (depth > 0 || + s->ssl->options.asyncState != TLS_ASYNC_FINALIZE)) { + return 1; + } + + if (ret == 0) { + ret = MbedVerifyChain(s, store); + } + MbedChainFree(s); + if (ret != 0) { + s->verify_ret = ret; + s->verify_flags = 0xFFFFFFFF; + } + return ret == 0 && (s->verify_flags == 0 || + s->authmode == MBEDTLS_SSL_VERIFY_OPTIONAL); +} + +static int MbedCertErr(int ret) +{ + return (ret == WC_NO_ERR_TRACE(MEMORY_E)) ? MBEDTLS_ERR_X509_ALLOC_FAILED : + MBEDTLS_ERR_X509_INVALID_FORMAT; +} + +/* Checks buf decodes as a certificate, without verifying it. Sets *len to + * the certificate's length, dropping any trailing data. */ +static int MbedCertCheck(const unsigned char* buf, size_t* len) +{ + DecodedCert* cert; + word32 idx = 0; + int seqLen; + int ret; + + if (GetSequence(buf, &idx, &seqLen, (word32)*len) < 0) { + return MBEDTLS_ERR_X509_INVALID_FORMAT; + } + + *len = idx + (word32)seqLen; + cert = (DecodedCert*)XMALLOC(sizeof(*cert), NULL, DYNAMIC_TYPE_DCERT); + if (cert == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + ret = MbedCertDecode(cert, buf, (word32)*len); + wc_FreeDecodedCert(cert); + XFREE(cert, NULL, DYNAMIC_TYPE_DCERT); + + return (ret == 0) ? 0 : MbedCertErr(ret); +} + +void mbedtls_x509_crt_init(mbedtls_x509_crt* crt) +{ + WOLFSSL_ENTER("mbedtls_x509_crt_init"); + + if (crt != NULL) { + XMEMSET(crt, 0, sizeof(*crt)); + } +} + +void mbedtls_x509_crt_free(mbedtls_x509_crt* crt) +{ + mbedtls_x509_crt* cur = crt; + mbedtls_x509_crt* next; + + WOLFSSL_ENTER("mbedtls_x509_crt_free"); + + while (cur != NULL) { + next = cur->next; + XFREE(cur->raw.p, NULL, DYNAMIC_TYPE_CERT); + if (cur != crt) { + XFREE(cur, NULL, DYNAMIC_TYPE_X509); + } + cur = next; + } + if (crt != NULL) { + XMEMSET(crt, 0, sizeof(*crt)); + } +} + +/* Returns 0 if all certificates parse, the number that failed if some parse, + * else an error. Input without a PEM header is DER. Unlike Mbed TLS, PEM + * need not be NUL terminated. */ +int mbedtls_x509_crt_parse(mbedtls_x509_crt* chain, + const unsigned char* buf, size_t buflen) +{ + const unsigned char* pem = buf; + size_t len = buflen; + EncryptedInfo info; + DerBuffer* der; + int parsed = 0; + int failed = 0; + int ret; + + WOLFSSL_ENTER("mbedtls_x509_crt_parse"); + + if (chain == NULL || buf == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + while (len > 0) { + der = NULL; + XMEMSET(&info, 0, sizeof(info)); + ret = wc_PemToDer(pem, (long)len, CERT_TYPE, &der, NULL, &info, NULL); + if (ret == WC_NO_ERR_TRACE(ASN_NO_PEM_HEADER)) { + break; + } + if (ret == 0) { + ret = mbedtls_x509_crt_parse_der(chain, der->buffer, + der->length); + } + else { + ret = MbedCertErr(ret); + } + wc_FreeDer(&der); + if (ret == MBEDTLS_ERR_X509_ALLOC_FAILED) { + return ret; + } + if (ret == 0) { + parsed++; + } + else { + failed++; + } + /* Set once the footer is found, also when the block does not decode */ + if (info.consumed <= 0 || (size_t)info.consumed > len) { + break; + } + pem += info.consumed; + len -= (size_t)info.consumed; + } + + if (parsed > 0) { + ret = failed; + } + else if (failed > 0) { + ret = MBEDTLS_ERR_X509_INVALID_FORMAT; + } + else { + ret = mbedtls_x509_crt_parse_der(chain, buf, buflen); + } + return ret; +} + +int mbedtls_x509_crt_parse_der(mbedtls_x509_crt* chain, + const unsigned char* buf, size_t buflen) +{ + mbedtls_x509_crt* crt = chain; + unsigned char* copy; + int ret; + + WOLFSSL_ENTER("mbedtls_x509_crt_parse_der"); + + if (chain == NULL || buf == NULL || (word32)buflen != buflen) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + + ret = MbedCertCheck(buf, &buflen); + if (ret != 0) { + return ret; + } + + copy = (unsigned char*)XMALLOC(buflen, NULL, DYNAMIC_TYPE_CERT); + if (copy == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + XMEMCPY(copy, buf, buflen); + + while (crt->next != NULL) { + crt = crt->next; + } + + if (crt->raw.p != NULL) { + crt->next = (mbedtls_x509_crt*)XMALLOC(sizeof(*crt), NULL, + DYNAMIC_TYPE_X509); + if (crt->next == NULL) { + XFREE(copy, NULL, DYNAMIC_TYPE_CERT); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + crt = crt->next; + XMEMSET(crt, 0, sizeof(*crt)); + } + + crt->raw.p = copy; + crt->raw.len = buflen; + return 0; +} + +#if defined(MBEDTLS_FS_IO) +int mbedtls_x509_crt_parse_file(mbedtls_x509_crt* chain, const char* path) +{ + byte* buf = NULL; + size_t len = 0; + int ret; + + WOLFSSL_ENTER("mbedtls_x509_crt_parse_file"); + + if (wc_FileLoad(path, &buf, &len, NULL) != 0) { + return MBEDTLS_ERR_X509_FILE_IO_ERROR; + } + ret = mbedtls_x509_crt_parse(chain, buf, len); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} + +/* As in Mbed TLS, a file that does not parse counts as one failure */ +int mbedtls_x509_crt_parse_path(mbedtls_x509_crt* chain, const char* path) +{ +#ifndef NO_WOLFSSL_DIR + ReadDirCtx* dir; + char* name = NULL; + int failed = 0; + int ret; + + WOLFSSL_ENTER("mbedtls_x509_crt_parse_path"); + + if (chain == NULL || path == NULL) { + return MBEDTLS_ERR_X509_BAD_INPUT_DATA; + } + dir = (ReadDirCtx*)XMALLOC(sizeof(*dir), NULL, DYNAMIC_TYPE_DIRCTX); + if (dir == NULL) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + ret = wc_ReadDirFirst(dir, path, &name); + while (ret == 0) { + ret = mbedtls_x509_crt_parse_file(chain, name); + failed += (ret < 0) ? 1 : ret; + ret = wc_ReadDirNext(dir, path, &name); + } + wc_ReadDirClose(dir); + XFREE(dir, NULL, DYNAMIC_TYPE_DIRCTX); + + return (ret == WC_READDIR_NOFILE) ? failed : MBEDTLS_ERR_X509_FILE_IO_ERROR; +#else + (void)chain; + (void)path; + return MBEDTLS_ERR_X509_FILE_IO_ERROR; +#endif /* !NO_WOLFSSL_DIR */ +} +#endif /* MBEDTLS_FS_IO */ + +struct mbedtls_x509_crt_profile { + uint32_t rsa_min_bitlen; +}; + +const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_default = { 2048 }; +const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_next = { 2048 }; +const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb = { 0 }; + +void mbedtls_ssl_config_init(mbedtls_ssl_config* conf) +{ + WOLFSSL_ENTER("mbedtls_ssl_config_init"); + + if (conf != NULL) { + XMEMSET(conf, 0, sizeof(*conf)); + } +} + +void mbedtls_ssl_config_free(mbedtls_ssl_config* conf) +{ + WOLFSSL_ENTER("mbedtls_ssl_config_free"); + + if (conf != NULL) { + wolfSSL_CTX_free(conf->ctx); + XMEMSET(conf, 0, sizeof(*conf)); + } +} + +void mbedtls_ssl_conf_authmode(mbedtls_ssl_config* conf, int authmode) +{ + static const int modes[] = { + WOLFSSL_VERIFY_NONE, + WOLFSSL_VERIFY_PEER, + WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT + }; + + WOLFSSL_ENTER("mbedtls_ssl_conf_authmode"); + + if (conf == NULL) { + return; + } + if (conf->ctx == NULL || authmode < MBEDTLS_SSL_VERIFY_NONE || + authmode > MBEDTLS_SSL_VERIFY_REQUIRED) { + MbedConfErr(conf, MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + return; + } + conf->authmode = authmode; + wolfSSL_CTX_set_verify(conf->ctx, modes[authmode], + (authmode == MBEDTLS_SSL_VERIFY_NONE) ? NULL : MbedVerifyCb); +} + +int mbedtls_ssl_config_defaults(mbedtls_ssl_config* conf, + int endpoint, int transport, int preset) +{ + static const struct { + int endpoint; + int transport; + WOLFSSL_METHOD* (*method)(void); + } methods[] = { + #ifndef NO_WOLFSSL_CLIENT + { MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, + wolfTLS_client_method }, + #endif /* !NO_WOLFSSL_CLIENT */ + #ifndef NO_WOLFSSL_SERVER + { MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + wolfTLS_server_method }, + #endif /* !NO_WOLFSSL_SERVER */ + #if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_CLIENT) + { MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_DATAGRAM, + wolfDTLS_client_method }, + #endif /* WOLFSSL_DTLS && !NO_WOLFSSL_CLIENT */ + #if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER) + { MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_DATAGRAM, + wolfDTLS_server_method }, + #endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */ + }; + size_t i; + + WOLFSSL_ENTER("mbedtls_ssl_config_defaults"); + + if (conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + for (i = 0; i < XELEM_CNT(methods); i++) { + if (methods[i].endpoint == endpoint && + methods[i].transport == transport) { + break; + } + } + if (i == XELEM_CNT(methods) || preset != MBEDTLS_SSL_PRESET_DEFAULT) { + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + + wolfSSL_CTX_free(conf->ctx); + conf->err = 0; + conf->endpoint = endpoint; + conf->min_tls_version = MBEDTLS_SSL_VERSION_TLS1_2; + conf->max_tls_version = MBEDTLS_SSL_VERSION_TLS1_3; + conf->ciphersuites = NULL; + conf->ca_chain = NULL; + conf->ctx = wolfSSL_CTX_new(methods[i].method()); + if (conf->ctx == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + mbedtls_ssl_conf_authmode(conf, + (endpoint == MBEDTLS_SSL_IS_CLIENT) ? MBEDTLS_SSL_VERIFY_REQUIRED : + MBEDTLS_SSL_VERIFY_NONE); + return 0; +} + +void mbedtls_ssl_conf_ca_chain(mbedtls_ssl_config* conf, + mbedtls_x509_crt* ca_chain, mbedtls_x509_crl* ca_crl) +{ + const mbedtls_x509_crt* crt; + int ret = (ca_crl == NULL) ? 0 : MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + + WOLFSSL_ENTER("mbedtls_ssl_conf_ca_chain"); + + if (conf == NULL) { + return; + } + + if (ret == 0) { + ret = MbedRet(wolfSSL_CTX_UnloadCAs(conf->ctx), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + + /* Mbed TLS checks a CA only when it is used, so a CA that wolfSSL + * rejects on load, such as an expired one, is left out */ + for (crt = ca_chain; ret == 0 && crt != NULL; crt = crt->next) { + if (crt->raw.p != NULL && + wolfSSL_CTX_load_verify_buffer(conf->ctx, crt->raw.p, + (long)crt->raw.len, WOLFSSL_FILETYPE_ASN1) == + WC_NO_ERR_TRACE(MEMORY_E)) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + } + + conf->ca_chain = (ret == 0) ? ca_chain : NULL; + if (ret != 0) { + MbedConfErr(conf, ret); + } +} + +/* Mbed TLS sends every certificate in own_cert. A second call replaces the + * first, where Mbed TLS keeps both and picks one per handshake. */ +int mbedtls_ssl_conf_own_cert(mbedtls_ssl_config* conf, + mbedtls_x509_crt* own_cert, mbedtls_pk_context* pk_key) +{ + const mbedtls_x509_crt* crt; + unsigned char* chain; + size_t len = 0; + int ret; + + WOLFSSL_ENTER("mbedtls_ssl_conf_own_cert"); + + if (conf == NULL || conf->ctx == NULL || own_cert == NULL || + own_cert->raw.p == NULL || pk_key == NULL || pk_key->der == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + + for (crt = own_cert; crt != NULL; crt = crt->next) { + len += crt->raw.len; + } + chain = (unsigned char*)XMALLOC(len, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (chain == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + len = 0; + for (crt = own_cert; crt != NULL; crt = crt->next) { + XMEMCPY(chain + len, crt->raw.p, crt->raw.len); + len += crt->raw.len; + } + + ret = MbedRet(wolfSSL_CTX_use_certificate_chain_buffer_format(conf->ctx, + chain, (long)len, WOLFSSL_FILETYPE_ASN1), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + XFREE(chain, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (ret == 0) { + ret = MbedRet(wolfSSL_CTX_use_PrivateKey_buffer(conf->ctx, + pk_key->der, (long)pk_key->der_len, WOLFSSL_FILETYPE_ASN1), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + return ret; +} + +/* Without MBEDTLS_DEBUG_C, Mbed TLS never calls f_dbg either */ +void mbedtls_ssl_conf_dbg(mbedtls_ssl_config* conf, + void (*f_dbg)(void*, int, const char*, int, const char*), void* p_dbg) +{ + WOLFSSL_ENTER("mbedtls_ssl_conf_dbg"); + + (void)conf; + (void)f_dbg; + (void)p_dbg; +} + +void mbedtls_ssl_conf_min_tls_version(mbedtls_ssl_config* conf, + mbedtls_ssl_protocol_version tls_version) +{ + WOLFSSL_ENTER("mbedtls_ssl_conf_min_tls_version"); + + if (conf != NULL) { + conf->min_tls_version = tls_version; + } +} + +void mbedtls_ssl_conf_max_tls_version(mbedtls_ssl_config* conf, + mbedtls_ssl_protocol_version tls_version) +{ + WOLFSSL_ENTER("mbedtls_ssl_conf_max_tls_version"); + + if (conf != NULL) { + conf->max_tls_version = tls_version; + } +} + +/* Not copied: the list must stay valid while conf is in use */ +void mbedtls_ssl_conf_ciphersuites(mbedtls_ssl_config* conf, + const int* ciphersuites) +{ + WOLFSSL_ENTER("mbedtls_ssl_conf_ciphersuites"); + + if (conf != NULL) { + conf->ciphersuites = ciphersuites; + } +} + +void mbedtls_ssl_conf_verify(mbedtls_ssl_config* conf, + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*), void* p_vrfy) +{ + WOLFSSL_ENTER("mbedtls_ssl_conf_verify"); + + if (conf != NULL) { + conf->f_vrfy = f_vrfy; + conf->p_vrfy = p_vrfy; + } +} + +void mbedtls_ssl_conf_cert_profile(mbedtls_ssl_config* conf, + const mbedtls_x509_crt_profile* profile) +{ + WOLFSSL_ENTER("mbedtls_ssl_conf_cert_profile"); + + (void)conf; + (void)profile; +} + +#define MBED_SUITES_MAX (WOLFSSL_MAX_SUITE_SZ / 2) +#define MBED_SUITE_NAME_SZ 64 + +static int mbedSuiteIds[MBED_SUITES_MAX + 1]; +static char mbedSuiteNames[MBED_SUITES_MAX][MBED_SUITE_NAME_SZ]; +static int mbedSuitesSet = 0; + +/* TLS1-3-AES-128-GCM-SHA256 for TLS_AES_128_GCM_SHA256, else the IANA name + * with dashes */ +static void MbedSuiteName(byte cs0, byte cs, char* name, size_t sz) +{ + const char* iana = wolfSSL_get_cipher_name_iana_from_suite(cs0, cs); + size_t i = 0; + + if (iana == NULL) { + iana = "unknown"; + } + if (cs0 == TLS13_BYTE && XSTRNCMP(iana, "TLS_", 4) == 0) { + XMEMCPY(name, "TLS1-3", 6); + i = 6; + iana += 3; + } + for (; i + 1 < sz && *iana != '\0'; i++, iana++) { + name[i] = (*iana == '_') ? '-' : *iana; + } + name[i] = '\0'; +} + +/* wolfSSL's default client suites, most preferred first. Filled on first use, + * as Mbed TLS does. */ +const int* mbedtls_ssl_list_ciphersuites(void) +{ + WOLFSSL_CTX* ctx; + WOLFSSL* ssl = NULL; + const Suites* suites = NULL; + size_t n = 0; + word16 i; + byte cs0; + byte cs; + + WOLFSSL_ENTER("mbedtls_ssl_list_ciphersuites"); + + if (mbedSuitesSet) { + return mbedSuiteIds; + } + ctx = wolfSSL_CTX_new(wolfTLS_client_method()); + if (ctx != NULL) { + ssl = wolfSSL_new(ctx); + } + if (ssl != NULL) { + suites = WOLFSSL_SUITES(ssl); + } + if (suites != NULL) { + for (i = 0; i + 1 < suites->suiteSz && n < MBED_SUITES_MAX; i += 2) { + cs0 = suites->suites[i]; + cs = suites->suites[i + 1]; + if (cs0 == CIPHER_BYTE && cs == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) { + continue; + } + mbedSuiteIds[n] = (cs0 << 8) | cs; + MbedSuiteName(cs0, cs, mbedSuiteNames[n], + sizeof(mbedSuiteNames[n])); + n++; + } + mbedSuitesSet = 1; + } + mbedSuiteIds[n] = 0; + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + return mbedSuiteIds; +} + +const char* mbedtls_ssl_get_ciphersuite_name(const int ciphersuite_id) +{ + const int* ids = mbedtls_ssl_list_ciphersuites(); + size_t i; + + WOLFSSL_ENTER("mbedtls_ssl_get_ciphersuite_name"); + + for (i = 0; ids[i] != 0; i++) { + if (ids[i] == ciphersuite_id) { + return mbedSuiteNames[i]; + } + } + return "unknown"; +} + +void mbedtls_ssl_init(mbedtls_ssl_context* ssl) +{ + WOLFSSL_ENTER("mbedtls_ssl_init"); + + if (ssl != NULL) { + XMEMSET(ssl, 0, sizeof(*ssl)); + } +} + +void mbedtls_ssl_free(mbedtls_ssl_context* ssl) +{ + WOLFSSL_ENTER("mbedtls_ssl_free"); + + if (ssl != NULL) { + MbedChainFree(ssl); + wolfSSL_free(ssl->ssl); + XFREE(ssl->hostname, NULL, DYNAMIC_TYPE_DOMAIN); + XMEMSET(ssl, 0, sizeof(*ssl)); + } +} + +/* Eight groups of four hex digits, seven colons and the NUL */ +#define MBED_IP_TEXT_SZ 40 + +/* The IP address in host as wolfSSL prints a certificate's IP addresses for + * its name check: dotted decimal, or eight groups of four hex digits. + * Returns 0 when host is not an IP address. */ +static int MbedIpText(const char* host, char* text, size_t sz) +{ + byte ip[16]; + int len = 0; + int i; + +#ifdef AF_INET6 + if (XINET_PTON(AF_INET, host, ip) == 1) { + len = XSNPRINTF(text, sz, "%u.%u.%u.%u", (unsigned)ip[0], + (unsigned)ip[1], (unsigned)ip[2], (unsigned)ip[3]); + } + else if (XINET_PTON(AF_INET6, host, ip) == 1) { + for (i = 0; i < 8; i++) { + len += XSNPRINTF(text + len, sz - (size_t)len, "%02X%02X%s", + (unsigned)ip[2 * i], (unsigned)ip[2 * i + 1], + (i < 7) ? ":" : ""); + } + } +#else + (void)host; + (void)text; + (void)sz; + (void)ip; + (void)i; +#endif /* AF_INET6 */ + return len; +} + +/* As in Mbed TLS, an IP address is also sent as the server name */ +static int MbedSetHostname(WOLFSSL* ssl, const char* hostname) +{ + char ip[MBED_IP_TEXT_SZ]; + int ret = 0; + + if (hostname == NULL) { + return 0; + } +#ifdef HAVE_SNI + ret = MbedRet(wolfSSL_UseSNI(ssl, WOLFSSL_SNI_HOST_NAME, hostname, + (word16)XSTRLEN(hostname)), MBEDTLS_ERR_SSL_BAD_INPUT_DATA); +#endif /* HAVE_SNI */ + if (ret == 0 && MbedIpText(hostname, ip, sizeof(ip)) > 0) { + ret = MbedRet(wolfSSL_check_ip_address(ssl, ip), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + else if (ret == 0) { + ret = MbedRet(wolfSSL_check_domain_name(ssl, hostname), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + return ret; +} + +/* wolfSSL takes one version, or the whole range when min and max differ */ +static int MbedSetVersion(WOLFSSL* ssl, const mbedtls_ssl_config* conf) +{ + static const struct { + mbedtls_ssl_protocol_version mbed; + int wolf; + } map[] = { + { MBEDTLS_SSL_VERSION_TLS1_2, WOLFSSL_TLSV1_2 }, + { MBEDTLS_SSL_VERSION_TLS1_3, WOLFSSL_TLSV1_3 } + }; + size_t i; + + if (conf->min_tls_version > conf->max_tls_version) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (conf->min_tls_version != conf->max_tls_version) { + return 0; + } + for (i = 0; i < XELEM_CNT(map); i++) { + if (map[i].mbed == conf->max_tls_version) { + return MbedRet(wolfSSL_SetVersion(ssl, map[i].wolf), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); + } + } + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; +} + +/* After MbedSetVersion, which resets the suites */ +static int MbedSetSuites(WOLFSSL* ssl, const int* ids) +{ + byte suites[WOLFSSL_MAX_SUITE_SZ]; + int len = 0; + + if (ids == NULL) { + return 0; + } + for (; *ids != 0 && len + 2 <= (int)sizeof(suites); ids++) { + suites[len++] = (byte)(*ids >> 8); + suites[len++] = (byte)*ids; + } + return MbedRet(wolfSSL_set_cipher_list_bytes(ssl, suites, len), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); +} + +/* Replaces the WOLFSSL with a new one from the config, with this context's + * callbacks and host name */ +static int MbedSslNew(mbedtls_ssl_context* ssl) +{ + int ret; + + if (ssl->conf->err != 0) { + return ssl->conf->err; + } + if (ssl->conf->ctx == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + MbedChainFree(ssl); + wolfSSL_free(ssl->ssl); + ssl->ssl = wolfSSL_new(ssl->conf->ctx); + if (ssl->ssl == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + ssl->authmode = ssl->conf->authmode; + ssl->verify_flags = 0; + ssl->verify_ret = 0; + ssl->peer_cert = 0; + wolfSSL_SSLSetIORecv(ssl->ssl, MbedRecvCb); + wolfSSL_SSLSetIOSend(ssl->ssl, MbedSendCb); + wolfSSL_SetIOReadCtx(ssl->ssl, ssl); + wolfSSL_SetIOWriteCtx(ssl->ssl, ssl); + wolfSSL_SetCertCbCtx(ssl->ssl, ssl); + ret = MbedSetVersion(ssl->ssl, ssl->conf); + if (ret == 0) { + ret = MbedSetSuites(ssl->ssl, ssl->conf->ciphersuites); + } + if (ret == 0) { + ret = MbedSetHostname(ssl->ssl, ssl->hostname); + } + return ret; +} + +int mbedtls_ssl_setup(mbedtls_ssl_context* ssl, const mbedtls_ssl_config* conf) +{ + WOLFSSL_ENTER("mbedtls_ssl_setup"); + + if (ssl == NULL || conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + ssl->conf = conf; + return MbedSslNew(ssl); +} + +/* conf_* calls made since mbedtls_ssl_setup take effect here */ +int mbedtls_ssl_session_reset(mbedtls_ssl_context* ssl) +{ + WOLFSSL_ENTER("mbedtls_ssl_session_reset"); + + if (ssl == NULL || ssl->conf == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + return MbedSslNew(ssl); +} + +void mbedtls_ssl_set_bio(mbedtls_ssl_context* ssl, void* p_bio, + mbedtls_ssl_send_t* f_send, mbedtls_ssl_recv_t* f_recv, + mbedtls_ssl_recv_timeout_t* f_recv_timeout) +{ + WOLFSSL_ENTER("mbedtls_ssl_set_bio"); + + if (ssl != NULL) { + ssl->p_bio = p_bio; + ssl->f_send = f_send; + ssl->f_recv = f_recv; + ssl->f_recv_timeout = f_recv_timeout; + } +} + +void mbedtls_ssl_set_verify(mbedtls_ssl_context* ssl, + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*), void* p_vrfy) +{ + WOLFSSL_ENTER("mbedtls_ssl_set_verify"); + + if (ssl != NULL) { + ssl->f_vrfy = f_vrfy; + ssl->p_vrfy = p_vrfy; + } +} + +/* Before mbedtls_ssl_setup too, as in Mbed TLS. NULL skips the name check. */ +int mbedtls_ssl_set_hostname(mbedtls_ssl_context* ssl, const char* hostname) +{ + char* copy = NULL; + size_t len; + + WOLFSSL_ENTER("mbedtls_ssl_set_hostname"); + + if (ssl == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (hostname != NULL) { + len = XSTRLEN(hostname); + if (len > MBEDTLS_SSL_MAX_HOST_NAME_LEN) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + copy = (char*)XMALLOC(len + 1, NULL, DYNAMIC_TYPE_DOMAIN); + if (copy == NULL) { + return MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + XMEMCPY(copy, hostname, len + 1); + } + XFREE(ssl->hostname, NULL, DYNAMIC_TYPE_DOMAIN); + ssl->hostname = copy; + ssl->hostname_set = 1; + + if (ssl->ssl == NULL) { + return 0; + } + /* wolfSSL cannot remove a host name, so NULL needs a new WOLFSSL */ + return (copy != NULL) ? MbedSetHostname(ssl->ssl, copy) : + MbedSslNew(ssl); +} + +/* As in Mbed TLS 4.x, a client that verifies the server needs + * mbedtls_ssl_set_hostname() first */ +static int MbedSslCheck(const mbedtls_ssl_context* ssl) +{ + if (ssl == NULL || ssl->conf == NULL || ssl->ssl == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + if (ssl->conf->endpoint == MBEDTLS_SSL_IS_CLIENT && + ssl->authmode == MBEDTLS_SSL_VERIFY_REQUIRED && + !ssl->hostname_set) { + return MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME; + } + return 0; +} + +int mbedtls_ssl_handshake(mbedtls_ssl_context* ssl) +{ + int ret; + + WOLFSSL_ENTER("mbedtls_ssl_handshake"); + + ret = MbedSslCheck(ssl); + if (ret != 0) { + return ret; + } + ret = wolfSSL_negotiate(ssl->ssl); + return (ret == WOLFSSL_SUCCESS) ? 0 : MbedSslErr(ssl, ret); +} + +/* All ones while no result is available, as in Mbed TLS */ +uint32_t mbedtls_ssl_get_verify_result(const mbedtls_ssl_context* ssl) +{ + WOLFSSL_ENTER("mbedtls_ssl_get_verify_result"); + + if (ssl == NULL || ssl->ssl == NULL) { + return 0xFFFFFFFF; + } + if (ssl->verify_flags != 0) { + return ssl->verify_flags; + } + if (!wolfSSL_is_init_finished(ssl->ssl)) { + return 0xFFFFFFFF; + } + if (ssl->authmode == MBEDTLS_SSL_VERIFY_NONE) { + return MBEDTLS_X509_BADCERT_SKIP_VERIFY; + } + return ssl->peer_cert ? 0 : MBEDTLS_X509_BADCERT_MISSING; +} + +/* The IANA name, such as TLS_AES_128_GCM_SHA256, where Mbed TLS has its own, + * TLS1-3-AES-128-GCM-SHA256 */ +const char* mbedtls_ssl_get_ciphersuite(const mbedtls_ssl_context* ssl) +{ + WOLFSSL_ENTER("mbedtls_ssl_get_ciphersuite"); + + if (ssl == NULL || ssl->ssl == NULL) { + return NULL; + } + return wolfSSL_get_cipher(ssl->ssl); +} + +int mbedtls_ssl_get_ciphersuite_id_from_ssl(const mbedtls_ssl_context* ssl) +{ + WOLFSSL_ENTER("mbedtls_ssl_get_ciphersuite_id_from_ssl"); + + if (ssl == NULL || ssl->ssl == NULL) { + return 0; + } + return wolfSSL_get_current_cipher_suite(ssl->ssl); +} + +const char* mbedtls_ssl_get_version(const mbedtls_ssl_context* ssl) +{ + WOLFSSL_ENTER("mbedtls_ssl_get_version"); + + if (ssl == NULL || ssl->ssl == NULL) { + return "unknown"; + } + return wolfSSL_get_version(ssl->ssl); +} + +/* wolfSSL_version() returns the same IETF values */ +mbedtls_ssl_protocol_version mbedtls_ssl_get_version_number( + const mbedtls_ssl_context* ssl) +{ + int version; + + WOLFSSL_ENTER("mbedtls_ssl_get_version_number"); + + if (ssl == NULL || ssl->ssl == NULL) { + return MBEDTLS_SSL_VERSION_UNKNOWN; + } + version = wolfSSL_version(ssl->ssl); + if (version != MBEDTLS_SSL_VERSION_TLS1_2 && + version != MBEDTLS_SSL_VERSION_TLS1_3) { + return MBEDTLS_SSL_VERSION_UNKNOWN; + } + return (mbedtls_ssl_protocol_version)version; +} + +size_t mbedtls_ssl_get_bytes_avail(const mbedtls_ssl_context* ssl) +{ + int ret; + + WOLFSSL_ENTER("mbedtls_ssl_get_bytes_avail"); + + if (ssl == NULL || ssl->ssl == NULL) { + return 0; + } + ret = wolfSSL_pending(ssl->ssl); + return (ret > 0) ? (size_t)ret : 0; +} + +int mbedtls_ssl_read(mbedtls_ssl_context* ssl, unsigned char* buf, size_t len) +{ + int ret; + + WOLFSSL_ENTER("mbedtls_ssl_read"); + + ret = MbedSslCheck(ssl); + if (ret != 0) { + return ret; + } + ret = wolfSSL_read(ssl->ssl, buf, (int)len); + if (ret > 0) { + return ret; + } + ret = MbedSslErr(ssl, ret); + /* As in Mbed TLS, EOF without close_notify reads 0 */ + return (ret == MBEDTLS_ERR_SSL_CONN_EOF) ? 0 : ret; +} + +int mbedtls_ssl_write(mbedtls_ssl_context* ssl, + const unsigned char* buf, size_t len) +{ + int ret; + + WOLFSSL_ENTER("mbedtls_ssl_write"); + + ret = MbedSslCheck(ssl); + if (ret != 0) { + return ret; + } + ret = wolfSSL_write(ssl->ssl, buf, (int)len); + return (ret > 0) ? ret : MbedSslErr(ssl, ret); +} + +/* Mbed TLS sends close_notify without waiting for the peer's reply */ +int mbedtls_ssl_close_notify(mbedtls_ssl_context* ssl) +{ + int ret; + + WOLFSSL_ENTER("mbedtls_ssl_close_notify"); + + if (ssl == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + ret = wolfSSL_shutdown(ssl->ssl); + if (ret == WOLFSSL_SUCCESS || + ret == WC_NO_ERR_TRACE(WOLFSSL_SHUTDOWN_NOT_DONE)) { + return 0; + } + return MbedSslErr(ssl, ret); +} + +void mbedtls_ssl_session_init(mbedtls_ssl_session* session) +{ + WOLFSSL_ENTER("mbedtls_ssl_session_init"); + + if (session != NULL) { + XMEMSET(session, 0, sizeof(*session)); + } +} + +void mbedtls_ssl_session_free(mbedtls_ssl_session* session) +{ + WOLFSSL_ENTER("mbedtls_ssl_session_free"); + + if (session != NULL) { + wolfSSL_SESSION_free(session->session); + XMEMSET(session, 0, sizeof(*session)); + } +} + +int mbedtls_ssl_get_session(const mbedtls_ssl_context* ssl, + mbedtls_ssl_session* session) +{ + WOLFSSL_ENTER("mbedtls_ssl_get_session"); + + if (ssl == NULL || ssl->ssl == NULL || session == NULL || + session->session != NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + session->session = wolfSSL_get1_session(ssl->ssl); + return (session->session != NULL) ? 0 : MBEDTLS_ERR_SSL_BAD_INPUT_DATA; +} + +int mbedtls_ssl_set_session(mbedtls_ssl_context* ssl, + const mbedtls_ssl_session* session) +{ + WOLFSSL_ENTER("mbedtls_ssl_set_session"); + + if (ssl == NULL || ssl->ssl == NULL || session == NULL || + session->session == NULL) { + return MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + } + return MbedRet(wolfSSL_set_session(ssl->ssl, session->session), + MBEDTLS_ERR_SSL_BAD_INPUT_DATA); +} + +/* Serializing a session needs HAVE_EXT_CACHE */ +int mbedtls_ssl_session_save(const mbedtls_ssl_session* session, + unsigned char* buf, size_t buf_len, size_t* olen) +{ + WOLFSSL_ENTER("mbedtls_ssl_session_save"); + + (void)session; + (void)buf; + (void)buf_len; + if (olen != NULL) { + *olen = 0; + } + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} + +int mbedtls_ssl_session_load(mbedtls_ssl_session* session, + const unsigned char* buf, size_t len) +{ + WOLFSSL_ENTER("mbedtls_ssl_session_load"); + + (void)session; + (void)buf; + (void)len; + return MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; +} + +#endif /* WOLFPSA_MBEDTLS_COMPAT */ diff --git a/src/mbedtls_compat_internal.h b/src/mbedtls_compat_internal.h new file mode 100644 index 00000000..80c8d64b --- /dev/null +++ b/src/mbedtls_compat_internal.h @@ -0,0 +1,38 @@ +/* mbedtls_compat_internal.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Shared by mbedtls_compat.c and mbedtls_net.c. Not part of the public + * API. */ + +#ifndef MBEDTLS_COMPAT_INTERNAL_H +#define MBEDTLS_COMPAT_INTERNAL_H + +#include + +enum { + MBED_IO_RECV = 0, + MBED_IO_SEND = 1 +}; + +/* I/O error code in one direction, toWolf from Mbed TLS to wolfSSL */ +WOLFSSL_LOCAL int MbedIoErr(int code, int dir, int toWolf); + +#endif /* MBEDTLS_COMPAT_INTERNAL_H */ diff --git a/src/mbedtls_net.c b/src/mbedtls_net.c new file mode 100644 index 00000000..8f185d23 --- /dev/null +++ b/src/mbedtls_net.c @@ -0,0 +1,281 @@ +/* mbedtls_net.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Mbed TLS 4.x network API on wolfSSL's sockets (MBEDTLS_COMPAT=1). */ + +/* getaddrinfo() with -std=c99 */ +#if defined(WOLFPSA_MBEDTLS_COMPAT) && !defined(_POSIX_C_SOURCE) + #define _POSIX_C_SOURCE 200112L +#endif /* WOLFPSA_MBEDTLS_COMPAT && !_POSIX_C_SOURCE */ + +#ifdef HAVE_CONFIG_H + #include +#endif /* HAVE_CONFIG_H */ + +#include "psa_config.h" + +#if defined(WOLFPSA_MBEDTLS_COMPAT) + +#include +#include +#include +#include +#include "mbedtls_compat_internal.h" +#if defined(USE_WOLFSSL_IO) && !defined(USE_WINDOWS_API) + #include + #include + #include +#endif /* USE_WOLFSSL_IO && !USE_WINDOWS_API */ + +void mbedtls_net_init(mbedtls_net_context* ctx) +{ + WOLFSSL_ENTER("mbedtls_net_init"); + + if (ctx != NULL) { + ctx->fd = -1; + } +} + +#ifdef USE_WOLFSSL_IO +static int MbedNetBind(int fd, const struct addrinfo* ai, int proto) +{ + int on = 1; + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, + (XSOCKLENT)sizeof(on)) != 0) { + return MBEDTLS_ERR_NET_SOCKET_FAILED; + } + if (bind(fd, ai->ai_addr, (XSOCKLENT)ai->ai_addrlen) != 0) { + return MBEDTLS_ERR_NET_BIND_FAILED; + } + if (proto == MBEDTLS_NET_PROTO_TCP && + listen(fd, MBEDTLS_NET_LISTEN_BACKLOG) != 0) { + return MBEDTLS_ERR_NET_LISTEN_FAILED; + } + return 0; +} + +/* Connects to host, or binds to it when server is set. A server with no + * host binds to every address. */ +static int MbedNetOpen(mbedtls_net_context* ctx, const char* host, + const char* port, int proto, int server) +{ + struct addrinfo hints; + struct addrinfo* list = NULL; + struct addrinfo* ai; + int ret = MBEDTLS_ERR_NET_UNKNOWN_HOST; + + if (ctx == NULL || (host == NULL && !server) || port == NULL) { + return MBEDTLS_ERR_NET_INVALID_CONTEXT; + } + XMEMSET(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + if (proto == MBEDTLS_NET_PROTO_UDP) { + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + } + else { + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + } + if (host == NULL) { + hints.ai_flags = AI_PASSIVE; + } + +#ifndef USE_WINDOWS_API + /* As in Mbed TLS: a send to a closed peer fails instead of ending the + * process */ + (void)signal(SIGPIPE, SIG_IGN); +#endif /* !USE_WINDOWS_API */ + + if (getaddrinfo(host, port, &hints, &list) != 0) { + return MBEDTLS_ERR_NET_UNKNOWN_HOST; + } + + for (ai = list; ai != NULL; ai = ai->ai_next) { + ctx->fd = (int)socket(ai->ai_family, ai->ai_socktype, + ai->ai_protocol); + if (ctx->fd < 0) { + ret = MBEDTLS_ERR_NET_SOCKET_FAILED; + continue; + } + if (server) { + ret = MbedNetBind(ctx->fd, ai, proto); + } + else if (connect(ctx->fd, ai->ai_addr, + (XSOCKLENT)ai->ai_addrlen) == 0) { + ret = 0; + } + else { + ret = MBEDTLS_ERR_NET_CONNECT_FAILED; + } + if (ret == 0) { + break; + } + CloseSocket(ctx->fd); + ctx->fd = -1; + } + + freeaddrinfo(list); + return ret; +} + +int mbedtls_net_connect(mbedtls_net_context* ctx, const char* host, + const char* port, int proto) +{ + WOLFSSL_ENTER("mbedtls_net_connect"); + + return MbedNetOpen(ctx, host, port, proto, 0); +} + +int mbedtls_net_bind(mbedtls_net_context* ctx, const char* bind_ip, + const char* port, int proto) +{ + WOLFSSL_ENTER("mbedtls_net_bind"); + + return MbedNetOpen(ctx, bind_ip, port, proto, 1); +} + +/* The last socket call failed only because it would block */ +static int MbedNetWouldBlock(void) +{ +#ifdef USE_WINDOWS_API + int err = WSAGetLastError(); +#else + int err = errno; +#endif /* USE_WINDOWS_API */ + + return err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN; +} + +/* TCP only. client_ip gets the 4 or 16 bytes of the client's address. */ +int mbedtls_net_accept(mbedtls_net_context* bind_ctx, + mbedtls_net_context* client_ctx, void* client_ip, size_t buf_size, + size_t* cip_len) +{ + struct sockaddr_storage addr; + XSOCKLENT addrLen = (XSOCKLENT)sizeof(addr); + const void* ip; + size_t ipLen; + + WOLFSSL_ENTER("mbedtls_net_accept"); + + if (bind_ctx == NULL || client_ctx == NULL || bind_ctx->fd < 0) { + return MBEDTLS_ERR_NET_INVALID_CONTEXT; + } + client_ctx->fd = (int)accept(bind_ctx->fd, (struct sockaddr*)&addr, + &addrLen); + if (client_ctx->fd < 0) { + return MbedNetWouldBlock() ? MBEDTLS_ERR_SSL_WANT_READ : + MBEDTLS_ERR_NET_ACCEPT_FAILED; + } + if (client_ip == NULL) { + return 0; + } + + if (addr.ss_family == AF_INET) { + ip = &((struct sockaddr_in*)&addr)->sin_addr; + ipLen = sizeof(struct in_addr); + } + else { + ip = &((struct sockaddr_in6*)&addr)->sin6_addr; + ipLen = sizeof(struct in6_addr); + } + *cip_len = ipLen; + if (buf_size < ipLen) { + return MBEDTLS_ERR_NET_BUFFER_TOO_SMALL; + } + XMEMCPY(client_ip, ip, ipLen); + return 0; +} + +void mbedtls_net_usleep(unsigned long usec) +{ + WOLFSSL_ENTER("mbedtls_net_usleep"); + +#ifdef USE_WINDOWS_API + Sleep((DWORD)((usec + 999) / 1000)); +#else + struct timeval tv; + + tv.tv_sec = (time_t)(usec / 1000000); + tv.tv_usec = (suseconds_t)(usec % 1000000); + (void)select(0, NULL, NULL, NULL, &tv); +#endif /* USE_WINDOWS_API */ +} + +void mbedtls_net_close(mbedtls_net_context* ctx) +{ + WOLFSSL_ENTER("mbedtls_net_close"); + + if (ctx != NULL && ctx->fd >= 0) { + CloseSocket(ctx->fd); + ctx->fd = -1; + } +} + +/* Also ends the connection for a forked process that shares the socket */ +void mbedtls_net_free(mbedtls_net_context* ctx) +{ + WOLFSSL_ENTER("mbedtls_net_free"); + + if (ctx != NULL && ctx->fd >= 0) { + (void)shutdown(ctx->fd, 2); + } + mbedtls_net_close(ctx); +} + +int mbedtls_net_recv(void* ctx, unsigned char* buf, size_t len) +{ + mbedtls_net_context* net = (mbedtls_net_context*)ctx; + int ret; + + WOLFSSL_ENTER("mbedtls_net_recv"); + + if (net == NULL || net->fd < 0) { + return MBEDTLS_ERR_NET_INVALID_CONTEXT; + } + ret = wolfIO_Recv(net->fd, (char*)buf, (int)len, 0); + if (ret >= 0) { + return ret; + } + return MbedIoErr(ret, MBED_IO_RECV, 0); +} + +int mbedtls_net_send(void* ctx, const unsigned char* buf, size_t len) +{ + mbedtls_net_context* net = (mbedtls_net_context*)ctx; + int ret; + + WOLFSSL_ENTER("mbedtls_net_send"); + + if (net == NULL || net->fd < 0) { + return MBEDTLS_ERR_NET_INVALID_CONTEXT; + } + ret = wolfIO_Send(net->fd, (char*)(wc_ptr_t)buf, (int)len, 0); + if (ret >= 0) { + return ret; + } + return MbedIoErr(ret, MBED_IO_SEND, 0); +} +#endif /* USE_WOLFSSL_IO */ + +#endif /* WOLFPSA_MBEDTLS_COMPAT */ diff --git a/test/Makefile b/test/Makefile index 95142815..9abfaba9 100644 --- a/test/Makefile +++ b/test/Makefile @@ -57,8 +57,8 @@ USER_SETTINGS_PATH ?= $(WOLFPSA_PATH) WOLFSSL_CPPFLAGS ?= -DWOLFSSL_USER_SETTINGS CPPFLAGS += -DHAVE_CONFIG_H -DWOLFSSL_HAVE_PSA -DHAVE_PK_CALLBACKS -# Mirrors the library's ENROLLMENT_ALG=1, which psa_api_test checks -ifeq ($(ENROLLMENT_ALG),1) +# Mirrors the library's ENROLLMENT_ALG=1 (MBEDTLS_COMPAT=1 implies it) +ifneq ($(filter 1,$(ENROLLMENT_ALG) $(MBEDTLS_COMPAT)),) CPPFLAGS += -DWOLFPSA_ENROLLMENT_ALG endif CPPFLAGS += -I$(WOLFSSL_PATH) -I$(WOLFSSL_PATH)/wolfssl diff --git a/test/mbedtls_compat/Makefile b/test/mbedtls_compat/Makefile new file mode 100644 index 00000000..3ea28aba --- /dev/null +++ b/test/mbedtls_compat/Makefile @@ -0,0 +1,121 @@ +# Build and run the Mbed TLS compatibility tests. Run by +# make MBEDTLS_COMPAT=1 mbedtls-compat-test in the wolfPSA root, which sets +# the paths, all absolute. + +ifndef WOLFPSA_PATH +$(error Run make MBEDTLS_COMPAT=1 mbedtls-compat-test in the wolfPSA root) +endif + +CFLAGS ?= -O2 +# Sources from other repositories build without -Werror +WARNFLAGS ?= -Wall + +# Clients use the layer in libwolfpsa.a. WOLFPSA_LDFLAGS has what its build +# needs at link, such as -fsanitize=address with ASAN=1. PSA_CONFIG_DIR has +# the generated PSA_WANT_* header. +WOLFPSA_LIB := $(WOLFPSA_PATH)/libwolfpsa.a +LAYER_FLAGS := -I$(WOLFPSA_PATH)/wolfpsa -I$(PSA_CONFIG_DIR) +LAYER_LIBS := $(WOLFPSA_LIB) -lm $(WOLFPSA_LDFLAGS) + +# The peers are the wolfSSL example server and client, from a wolfSSL +# configured out of tree from WOLFSSL_PATH, so they do not depend on wolfPSA's +# settings +WOLFSSL_BUILD := $(BUILD_DIR)/wolfssl +# Mbed TLS's test CA test-ca2.crt has NULL ECDSA parameters, which +# ssl_server sends in its chain +WOLFSSL_CONFIG := --enable-static --disable-shared \ + CPPFLAGS=-DWOLFSSL_ECC_SIGALG_PARAMS_NULL_ALLOWED +WOLFSSL_SERVER := $(WOLFSSL_BUILD)/examples/server/server +WOLFSSL_CLIENT := $(WOLFSSL_BUILD)/examples/client/client + +# Mbed TLS migration guide client +CLIENT := $(BUILD_DIR)/client-tls-mbedtls +# Certificate and key parsing, on Mbed TLS's test data +X509_TEST := $(BUILD_DIR)/x509_crt_test +PK_TEST := $(BUILD_DIR)/pk_test +# Return codes and verify results, client and server in one process +SSL_TEST := $(BUILD_DIR)/ssl_test +# Mbed TLS sample programs, built unchanged from MBEDTLS_PATH +PROGRAMS := $(addprefix $(BUILD_DIR)/programs/ssl/,mini_client ssl_client1 \ + ssl_server ssl_fork_server) +# Threads, with MBEDTLS_THREADING_C=1 +ifeq ($(MBEDTLS_THREADING_C),1) +LAYER_FLAGS += -DMBEDTLS_THREADING_C +LAYER_LIBS += -pthread +PROGRAMS += $(BUILD_DIR)/programs/ssl/ssl_pthread_server +endif +# Mbed TLS test certificates, data only. certs.c and the generated +# test_certs.h build against the Mbed TLS checkout's own headers. +CERTS := $(BUILD_DIR)/certs.o +CERTS_H := $(BUILD_DIR)/include/test/test_certs.h +MBEDTLS_FLAGS := -I$(BUILD_DIR)/include -I$(MBEDTLS_PATH)/include \ + -I$(MBEDTLS_PATH)/tests/include \ + $(addprefix -I$(MBEDTLS_PATH)/tf-psa-crypto/,include core \ + drivers/builtin/include) + +# $(call link,flags,libraries) links the C source and objects among the +# prerequisites +link = $(CC) $(1) $(CPPFLAGS) $(CFLAGS) $(WARNFLAGS) -o $@ \ + $(filter %.c %.o,$^) $(LDFLAGS) $(2) + +# Used by programs.sh +export BUILD_DIR WOLFSSL_PATH WOLFSSL_SERVER WOLFSSL_CLIENT MBEDTLS_PATH \ + MBEDTLS_THREADING_C + +.PHONY: check wolfssl-check FORCE + +check: $(CLIENT) $(X509_TEST) $(PK_TEST) $(SSL_TEST) $(PROGRAMS) \ + $(WOLFSSL_SERVER) $(WOLFSSL_CLIENT) + ./programs.sh + +$(CLIENT): client-tls-mbedtls.c +$(X509_TEST): x509_crt_test.c +$(PK_TEST): pk_test.c +$(SSL_TEST): ssl_test.c +$(PROGRAMS): $(BUILD_DIR)/programs/%: $(MBEDTLS_PATH)/programs/%.c +$(PROGRAMS): override CPPFLAGS += -I$(MBEDTLS_PATH)/tests/include +$(addprefix $(BUILD_DIR)/programs/ssl/,ssl_client1 ssl_server \ + ssl_fork_server ssl_pthread_server): $(CERTS) +$(CLIENT) $(X509_TEST) $(PK_TEST) $(SSL_TEST) $(PROGRAMS): \ + $(WOLFPSA_LIB) + @mkdir -p $(@D) + $(call link,$(LAYER_FLAGS),$(LAYER_LIBS)) + +$(CERTS_H): + @mkdir -p $(@D) + PYTHONPATH=$(MBEDTLS_PATH)/framework/scripts python3 \ + $(MBEDTLS_PATH)/framework/scripts/generate_test_cert_macros.py \ + --output $@ + +$(CERTS): $(MBEDTLS_PATH)/tests/src/certs.c $(CERTS_H) + $(CC) $(MBEDTLS_FLAGS) $(CPPFLAGS) $(CFLAGS) $(WARNFLAGS) -c -o $@ $< + +# configure refuses a clone that is configured in place. autogen.sh only adds +# files git ignores. +wolfssl-check: + @if [ -f $(WOLFSSL_PATH)/config.status ]; then \ + echo "Run make distclean in $(WOLFSSL_PATH)"; exit 1; \ + fi + +$(WOLFSSL_PATH)/configure: + cd $(@D) && ./autogen.sh + +# Contains WOLFSSL_PATH. Another path starts the build again. +WOLFSSL_STAMP := $(WOLFSSL_BUILD).path +$(WOLFSSL_STAMP): FORCE + @mkdir -p $(@D) + @if [ "$$(cat $@ 2>/dev/null)" != "$(WOLFSSL_PATH)" ]; then \ + rm -rf $(WOLFSSL_BUILD); echo "$(WOLFSSL_PATH)" > $@; \ + fi + +$(WOLFSSL_BUILD)/Makefile: Makefile $(WOLFSSL_STAMP) \ + $(WOLFSSL_PATH)/configure | wolfssl-check + @mkdir -p $(@D) + cd $(@D) && $(WOLFSSL_PATH)/configure CC="$(CC)" $(WOLFSSL_CONFIG) + +# One make builds both, so the library is not built twice at once +$(WOLFSSL_CLIENT): $(WOLFSSL_SERVER) +$(WOLFSSL_SERVER): $(WOLFSSL_BUILD)/Makefile FORCE + $(MAKE) -C $(WOLFSSL_BUILD) examples/server/server examples/client/client + +FORCE: diff --git a/test/mbedtls_compat/client-tls-mbedtls.c b/test/mbedtls_compat/client-tls-mbedtls.c new file mode 100644 index 00000000..667cd392 --- /dev/null +++ b/test/mbedtls_compat/client-tls-mbedtls.c @@ -0,0 +1,172 @@ +/* client-tls-mbedtls.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Mbed TLS 4.x client built on wolfSSL through the Mbed TLS compatibility + * headers. tls_get() is the code from the Mbed TLS migration guide. Run + * from the wolfSSL root, for its certs. */ + +/* the usual suspects */ +#include +#include +#include + +/* socket includes */ +#include +#include +#include +#include +#include + +/* Mbed TLS, provided by wolfPSA */ +#include +#include +#include +#include + +#define DEFAULT_PORT 11111 + +#define CA_FILE "certs/ca-cert.pem" +/* DNS name in the subjectAltName of certs/server-cert.pem */ +#define HOST_NAME "example.com" + +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; +} + +int main(int argc, char** argv) +{ + int sockfd; + struct sockaddr_in servAddr; + const char* caFile = CA_FILE; + const char* host = HOST_NAME; + int nonBlock = 0; + int opt; + int ret; + + while ((opt = getopt(argc, argv, "A:S:N")) != -1) { + if (opt == 'A') { + caFile = optarg; + } + else if (opt == 'S') { + host = optarg; + } + else if (opt == 'N') { + nonBlock = 1; + } + else { + break; + } + } + if (opt != -1 || optind != argc - 1) { + printf("usage: %s [-A ] [-S ] [-N] " + "\n", argv[0]); + return 1; + } + + memset(&servAddr, 0, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_port = htons(DEFAULT_PORT); + if (inet_pton(AF_INET, argv[optind], &servAddr.sin_addr) != 1) { + fprintf(stderr, "ERROR: invalid address\n"); + return 1; + } + + if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + fprintf(stderr, "ERROR: failed to create the socket\n"); + return 1; + } + if (connect(sockfd, (struct sockaddr*)&servAddr, sizeof(servAddr)) == -1) { + fprintf(stderr, "ERROR: failed to connect\n"); + close(sockfd); + return 1; + } + /* tls_get() retries on MBEDTLS_ERR_SSL_WANT_READ/WANT_WRITE */ + if (nonBlock && + fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK) != 0) { + fprintf(stderr, "ERROR: failed to set non-blocking\n"); + close(sockfd); + return 1; + } + + ret = tls_get(sockfd, host, caFile); + printf("tls_get() returned %d\n", ret); + + close(sockfd); + return (ret == 0) ? 0 : 1; +} diff --git a/test/mbedtls_compat/pk_test.c b/test/mbedtls_compat/pk_test.c new file mode 100644 index 00000000..d5ef1ad8 --- /dev/null +++ b/test/mbedtls_compat/pk_test.c @@ -0,0 +1,112 @@ +/* pk_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Private key parsing through the TF-PSA-Crypto 1.x API. The cases of + * mbedtls_pk_parse_keyfile come from TF-PSA-Crypto's + * tests/suites/test_suite_pkparse.data, those without a password. Run from + * framework/data_files in an Mbed TLS checkout. */ + +#include + +#include +#include + +#define INVALID MBEDTLS_ERR_PK_KEY_INVALID_FORMAT +#define ENCRYPTED MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE + +static const struct { + const char* file; + int ret; +} cases[] = { + { "ec_256_prv.pem", 0 }, + { "ec_256_prv.der", 0 }, + { "ec_256_prv.pk8.pem", 0 }, + { "ec_256_prv.pk8.der", 0 }, + { "ec_384_prv.pem", 0 }, + { "ec_521_prv.pem", 0 }, + { "ec_prv.pk8nopub.pem", 0 }, + { "ec_prv.pk8nopubparam.pem", 0 }, + { "ec_prv.pk8param.pem", 0 }, + { "format_gen.key", 0 }, + { "rsa_pkcs1_768_clear.der", 0 }, + { "rsa_pkcs1_776_clear.der", 0 }, + /* Not a multiple of 8 bits. Mbed TLS: INVALID */ + { "rsa_pkcs1_769_clear.der", 0 }, + /* Compressed public key. Mbed TLS: 0 */ + { "ec_256_prv.comp.pem", INVALID }, + /* Encrypted keys are not supported. Mbed TLS: PASSWORD_REQUIRED */ + { "rsa_pkcs8_pbes2_pbkdf2_1024_aes128cbc.pem", ENCRYPTED }, + /* Not in Mbed TLS's cases */ + { "server2.key", 0 }, + { "server5.crt", INVALID }, + { "missing.key", MBEDTLS_ERR_PK_FILE_IO_ERROR } +}; + +/* An empty buffer, and a second key into one context, as in Mbed TLS */ +static int BadInput(void) +{ + static const unsigned char der[] = { 0x30, 0x00 }; + mbedtls_pk_context pk; + int ok; + + mbedtls_pk_init(&pk); + ok = mbedtls_pk_parse_key(&pk, der, 0, NULL, 0) == INVALID && + mbedtls_pk_parse_keyfile(&pk, "server2.key", NULL) == 0 && + mbedtls_pk_parse_keyfile(&pk, "server5.key", NULL) == + PSA_ERROR_INVALID_ARGUMENT; + mbedtls_pk_free(&pk); + return ok; +} + +int main(void) +{ + mbedtls_pk_context pk; + size_t i; + int fails = 0; + int ret; + + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + mbedtls_pk_init(&pk); + ret = mbedtls_pk_parse_keyfile(&pk, cases[i].file, NULL); + if (ret == cases[i].ret) { + printf("PASS: %s\n", cases[i].file); + } + else { + printf("FAIL: %s: returned %d, expected %d\n", cases[i].file, + ret, cases[i].ret); + fails++; + } + mbedtls_pk_free(&pk); + } + if (BadInput()) { + printf("PASS: bad input\n"); + } + else { + printf("FAIL: bad input\n"); + fails++; + } + mbedtls_psa_crypto_free(); + return (fails == 0) ? 0 : 1; +} diff --git a/test/mbedtls_compat/programs.sh b/test/mbedtls_compat/programs.sh new file mode 100755 index 00000000..a5bf2024 --- /dev/null +++ b/test/mbedtls_compat/programs.sh @@ -0,0 +1,219 @@ +#!/bin/sh + +# Run x509_crt_test.c, pk_test.c and ssl_test.c on Mbed TLS's test data, then +# the Mbed TLS migration guide client, client-tls-mbedtls.c, and Mbed TLS's +# sample programs, built unchanged from MBEDTLS_PATH, against the wolfSSL +# example server, one server per case. A program missing a feature macro +# prints "... not defined" and exits 0, so that output fails the case, and a +# case can require a line of output. A case whose wrapper, such as faketime, +# is not installed is skipped. Then Mbed TLS's server programs, against the +# wolfSSL example client and ssl_client1. WRAP prefixes the programs. Run by +# the Makefile in this directory, which sets the paths. + +: "${BUILD_DIR:?}" "${WOLFSSL_PATH:?}" "${WOLFSSL_SERVER:?}" \ + "${WOLFSSL_CLIENT:?}" "${MBEDTLS_PATH:?}" + +DATA=$MBEDTLS_PATH/framework/data_files +READY=$BUILD_DIR/server-ready +# With ASAN=1: faketime preloads a library, which ASan refuses by default +export ASAN_OPTIONS="${ASAN_OPTIONS:+$ASAN_OPTIONS:}verify_asan_link_order=0" +FAILS=0 +SKIPS=0 + +if [ ! -f "$DATA/server5.crt" ]; then + echo "$DATA not found, run git submodule update --init in $MBEDTLS_PATH" + exit 1 +fi + +TESTS="x509_crt_test pk_test ssl_test" +if [ "$MBEDTLS_THREADING_C" != 1 ]; then + echo "SKIP: ssl_pthread_server: MBEDTLS_THREADING_C=1 not set" + SKIPS=$((SKIPS + 1)) +fi +for t in $TESTS; do + (cd "$DATA" && $WRAP "$BUILD_DIR/$t") || FAILS=$((FAILS + 1)) +done + +# The example server and the programs run from the wolfSSL root, for its certs +start_server() { + rm -f "$READY" + (cd "$WOLFSSL_PATH" && exec "$WOLFSSL_SERVER" -R "$READY" $1) \ + >/dev/null 2>&1 & + srv=$! + i=0 + while [ ! -f "$READY" ] && [ $i -lt 50 ]; do + i=$((i + 1)) + sleep 0.1 + done +} + +# The migration guide client, on its port, with certs/server-cert.pem +C=client-tls-mbedtls +LO=127.0.0.1 +TLS13="-p 11111 -d -v 4" +TLS12="-p 11111 -d -v 3" +GOT="tls_get() returned" +P=programs/ssl +# mini_client's built-in CA, test-ca2 from 2013, encodes its name as only the +# older server5 certs do, and wolfSSL compares encoded names +SERVER5="-p 4433 -d -c $DATA/server5-expired.crt -k $DATA/server5.key" +# Inside both validity periods: the CA ends 2023-09-22, the server 2014-03-08 +CA2_DATE="faketime 2014-01-01" +# Signed by test-ca-sha256.crt, in certs.c's mbedtls_test_cas_pem +SERVER2="-p 4433 -d -c $DATA/server2-sha256.crt -k $DATA/server2.key" +# The wolfSSL example server's own certificate, from another CA +SERVER_WOLF="-p 4433 -d" +VERIFY="Verifying peer X.509 certificate..." +# A CA that wolfSSL refuses to load, then the server's CA +BUNDLE=$BUILD_DIR/ca-bundle.pem +cat "$WOLFSSL_PATH/certs/test/expired/expired-ca.pem" \ + "$WOLFSSL_PATH/certs/ca-cert.pem" > "$BUNDLE" + +# name|program|arguments|server options|wrapper|expected exit code| +# expected output +while IFS='|' read -r name prog args sopts wrap expect want; do + [ -z "$name" ] && continue + if [ -n "$wrap" ] && ! command -v "${wrap%% *}" >/dev/null; then + echo "SKIP: $name: ${wrap%% *} not found" + SKIPS=$((SKIPS + 1)) + continue + fi + start_server "$sopts" + out=$(cd "$WOLFSSL_PATH" && $wrap $WRAP "$BUILD_DIR/$prog" $args 2>&1) + rc=$? + if [ $rc -eq "$expect" ] && ! echo "$out" | grep -q "not defined" && + echo "$out" | grep -qF -- "$want"; then + echo "PASS: $name" + else + echo "FAIL: $name: exit $rc: $out" + FAILS=$((FAILS + 1)) + fi + kill $srv 2>/dev/null + wait $srv 2>/dev/null +done <"$LOG" 2>&1 & + srv=$! + i=0 + while ! grep -q "Waiting for a remote connection" "$LOG" 2>/dev/null && + [ $i -lt 100 ]; do + i=$((i + 1)) + sleep 0.1 + done +} + +# Options for the wolfSSL example client, or a program +run_client() { + case "$1" in + -*) "$WOLFSSL_CLIENT" -p 4433 -g -A "$CAS" "$@" ;; + *) $WRAP "$BUILD_DIR/$1" ;; + esac +} + +LOG=$BUILD_DIR/server.log +# The server's response contains mbedtls_ssl_get_ciphersuite() +RESPONSE="Successful connection using: TLS" +# The server programs send their certificate and the CAs of certs.c's +# mbedtls_test_cas_pem, and the wolfSSL client checks each of them +CAS=$BUILD_DIR/test-cas.pem +cat "$DATA/test-ca-sha256.crt" "$DATA/test-ca-sha1.crt" \ + "$DATA/test-ca2.crt" > "$CAS" +THREADED= +if [ "$MBEDTLS_THREADING_C" = 1 ]; then + # ssl_pthread_server's limit, MAX_NUM_THREADS + FIVE="-v 4&-v 3&-v 4&-v 3&$P/ssl_client1" + THREADED="ssl_pthread_server, 5 clients at once|$P/ssl_pthread_server|$FIVE" +fi + +# Prints the output of a client that does not get the expected result. A +# client after ! must fail. +check_client() { + want=0 + c=$1 + case "$c" in + !*) want=1; c=${c#!} ;; + esac + cout=$(cd "$WOLFSSL_PATH" && run_client $c 2>&1) + rc=$? + got=1 + if [ $rc -eq 0 ] && echo "$cout" | grep -qF -- "$RESPONSE"; then + got=0 + fi + if [ $got -ne $want ]; then + printf '\nclient %s: exit %d: %s' "$c" $rc "$cout" + fi +} + +# name|program|clients, separated by ; and run one after another. Clients +# joined by & run at the same time. +while IFS='|' read -r name prog clients; do + [ -z "$name" ] && continue + start_program "$prog" + out= + IFS=';' + for step in $clients; do + IFS='&' + pids= + n=0 + for c in $step; do + unset IFS + check_client "$c" >"$LOG.$n" & + pids="$pids $!" + n=$((n + 1)) + done + wait $pids + for f in "$LOG".*; do + out="$out$(cat "$f")" + done + rm -f "$LOG".* + done + unset IFS + kill $srv 2>/dev/null + wait $srv 2>/dev/null + if [ -z "$out" ]; then + echo "PASS: $name" + else + echo "FAIL: $name:$out +server: $(cat "$LOG")" + FAILS=$((FAILS + 1)) + fi +done < +#include +#include + +#include +#include +#include +#include +#include + +#define CA_FILE "test-ca2.crt" +#define CERT_FILE "server5.crt" +#define KEY_FILE "server5.key" +/* server5.crt with server authentication as its only extended key usage */ +#define EKU_SRV_FILE "server5.eku-srv.crt" +/* localhost, two intermediate CAs, then test-ca.crt */ +#define CHAIN_FILE "server10_int3_int-ca2.crt" +#define LEAF_FILE "server10.crt" +#define LEAF_KEY_FILE "server10.key" +#define ROOT_FILE "test-ca.crt" +/* The CN of server5.crt and server10.crt */ +#define HOST_NAME "localhost" +/* Handshake steps for each side */ +#define MAX_STEPS 20 +/* Verify callback calls recorded */ +#define MAX_CALLS 8 +/* A verify callback's own return code */ +#define VERIFY_CB_ERR -0x1234 + +/* Data in one direction. closed is set by the writer. */ +typedef struct { + unsigned char buf[32768]; + size_t len; + int closed; +} Pipe; + +typedef struct { + mbedtls_ssl_config conf; + mbedtls_ssl_context ssl; + Pipe* rx; + Pipe* tx; +} Peer; + +/* The CAs a peer trusts, and the certificates and key a server sends */ +typedef struct { + mbedtls_x509_crt* ca; + mbedtls_x509_crt* own; + mbedtls_pk_context* key; +} Certs; + +/* What a verify callback saw and what it does. want is the certificate + * list expected at each depth. */ +typedef struct { + const mbedtls_x509_crt* want[MAX_CALLS]; + int depth[MAX_CALLS]; + uint32_t flags[MAX_CALLS]; + int calls; + int same; /* each certificate and the list after it was as expected */ + uint32_t clear; + uint32_t set; /* at depth 0 */ + int ret; +} Verify; + +static mbedtls_x509_crt ca; +static mbedtls_x509_crt cert; +static mbedtls_pk_context key; +static mbedtls_x509_crt root; +static mbedtls_x509_crt chain; +static mbedtls_x509_crt leaf; +static mbedtls_pk_context leafKey; +static mbedtls_x509_crt ekuSrv; +static const Certs defaultCerts = { &ca, &cert, &key }; +/* The client trusts the root of the chain the server sends */ +static const Certs chainCerts = { &root, &chain, &leafKey }; +/* The client does not trust it */ +static const Certs untrustedCerts = { &ca, &chain, &leafKey }; +static Pipe toClient; +static Pipe toServer; +static Peer cli; +static Peer srv; + +static int PipeSend(void* ctx, const unsigned char* buf, size_t len) +{ + Pipe* p = ((Peer*)ctx)->tx; + + if (len > sizeof(p->buf) - p->len) { + len = sizeof(p->buf) - p->len; + } + if (len == 0) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } + memcpy(p->buf + p->len, buf, len); + p->len += len; + return (int)len; +} + +static int PipeRecv(void* ctx, unsigned char* buf, size_t len) +{ + Pipe* p = ((Peer*)ctx)->rx; + + if (p->len == 0) { + return p->closed ? 0 : MBEDTLS_ERR_SSL_WANT_READ; + } + if (len > p->len) { + len = p->len; + } + memcpy(buf, p->buf, len); + memmove(p->buf, p->buf + len, p->len - len); + p->len -= len; + return (int)len; +} + +/* A client sets host, NULL included, unless setHost is 0 */ +static int PeerNewCerts(Peer* p, int endpoint, int authmode, + const Certs* certs, int setHost, const char* host) +{ + int ret; + + mbedtls_ssl_config_init(&p->conf); + mbedtls_ssl_init(&p->ssl); + p->rx = (endpoint == MBEDTLS_SSL_IS_CLIENT) ? &toClient : &toServer; + p->tx = (endpoint == MBEDTLS_SSL_IS_CLIENT) ? &toServer : &toClient; + memset(p->tx, 0, sizeof(*p->tx)); + + ret = mbedtls_ssl_config_defaults(&p->conf, endpoint, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (ret == 0) { + mbedtls_ssl_conf_authmode(&p->conf, authmode); + mbedtls_ssl_conf_ca_chain(&p->conf, certs->ca, NULL); + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + ret = mbedtls_ssl_conf_own_cert(&p->conf, certs->own, certs->key); + } + } + if (ret == 0) { + ret = mbedtls_ssl_setup(&p->ssl, &p->conf); + } + if (ret == 0 && setHost) { + ret = mbedtls_ssl_set_hostname(&p->ssl, host); + } + mbedtls_ssl_set_bio(&p->ssl, p, PipeSend, PipeRecv, NULL); + return ret; +} + +static int PeerNew(Peer* p, int endpoint, int authmode, int setHost, + const char* host) +{ + return PeerNewCerts(p, endpoint, authmode, &defaultCerts, setHost, host); +} + +static void PeerFree(Peer* p) +{ + mbedtls_ssl_free(&p->ssl); + mbedtls_ssl_config_free(&p->conf); +} + +static int Waiting(int ret) +{ + return ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE; +} + +/* The client's result. *srvRet gets the server's. */ +static int Handshake(int* srvRet) +{ + int c = MBEDTLS_ERR_SSL_WANT_READ; + int s = MBEDTLS_ERR_SSL_WANT_READ; + int i; + + for (i = 0; i < MAX_STEPS && (Waiting(c) || Waiting(s)); i++) { + if (Waiting(c)) { + c = mbedtls_ssl_handshake(&cli.ssl); + } + if (Waiting(s)) { + s = mbedtls_ssl_handshake(&srv.ssl); + } + } + *srvRet = s; + return c; +} + +/* Connects a client with VERIFY_REQUIRED and HOST_NAME */ +static int Connect(int srvAuthmode) +{ + int s; + + if (PeerNew(&cli, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_VERIFY_REQUIRED, 1, + HOST_NAME) != 0 || + PeerNew(&srv, MBEDTLS_SSL_IS_SERVER, srvAuthmode, 0, NULL) != 0) { + return -1; + } + return (Handshake(&s) == 0 && s == 0) ? 0 : -1; +} + +static int NoClientCertOptional(void) +{ + int ok = Connect(MBEDTLS_SSL_VERIFY_OPTIONAL) == 0 && + mbedtls_ssl_get_verify_result(&cli.ssl) == 0 && + mbedtls_ssl_get_verify_result(&srv.ssl) == + MBEDTLS_X509_BADCERT_MISSING; + + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +static int NoClientCertRequired(void) +{ + int s = 0; + int ok = PeerNew(&cli, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_VERIFY_REQUIRED, + 1, HOST_NAME) == 0 && + PeerNew(&srv, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_VERIFY_REQUIRED, 0, + NULL) == 0; + + if (ok) { + (void)Handshake(&s); + ok = s == MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE; + } + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* setHost 0 leaves the host name unset */ +static int ClientHost(int setHost, const char* first, const char* host, + int want) +{ + int s; + int ok = PeerNew(&cli, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_VERIFY_REQUIRED, + setHost, first) == 0 && + PeerNew(&srv, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_VERIFY_NONE, 0, + NULL) == 0; + + if (ok && setHost) { + ok = mbedtls_ssl_set_hostname(&cli.ssl, host) == 0; + } + if (ok) { + ok = Handshake(&s) == want; + } + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +static int HostNotSet(void) +{ + return ClientHost(0, NULL, NULL, + MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME); +} + +static int HostCleared(void) +{ + return ClientHost(1, "wrong.example.org", NULL, 0); +} + +static int HostReplaced(void) +{ + return ClientHost(1, "wrong.example.org", HOST_NAME, 0); +} + +static int HostWrong(void) +{ + return ClientHost(1, HOST_NAME, "wrong.example.org", + MBEDTLS_ERR_X509_CERT_VERIFY_FAILED); +} + +/* Compares crt and the certificates after it with want */ +static int SameList(const mbedtls_x509_crt* crt, const mbedtls_x509_crt* want) +{ + for (; crt != NULL && want != NULL; crt = crt->next, want = want->next) { + if (crt->raw.len != want->raw.len || + memcmp(crt->raw.p, want->raw.p, crt->raw.len) != 0) { + return 0; + } + } + return crt == NULL && want == NULL; +} + +static int VerifyCb(void* ctx, mbedtls_x509_crt* crt, int depth, + uint32_t* flags) +{ + Verify* v = (Verify*)ctx; + + if (v->calls < MAX_CALLS) { + v->depth[v->calls] = depth; + v->flags[v->calls] = *flags; + } + if (depth >= 0 && depth < MAX_CALLS && v->want[depth] != NULL) { + v->same &= SameList(crt, v->want[depth]); + } + v->calls++; + *flags &= ~v->clear; + if (depth == 0) { + *flags |= v->set; + } + return v->ret; +} + +static void VerifyInit(Verify* v) +{ + memset(v, 0, sizeof(*v)); + v->same = 1; +} + +/* The callback ran n times, from depth n - 1 down to 0 */ +static int TopDown(const Verify* v, int n) +{ + int i; + + for (i = 0; i < n && i < MAX_CALLS; i++) { + if (v->depth[i] != n - 1 - i) { + return 0; + } + } + return v->calls == n; +} + +/* A client with authmode, the CAs of certs, host and the verify callback v, + * and a server that sends the certificates of certs */ +static int VerifyPeers(int authmode, const Certs* certs, const char* host, + Verify* v) +{ + int ok = PeerNewCerts(&cli, MBEDTLS_SSL_IS_CLIENT, authmode, certs, 1, + host) == 0 && + PeerNewCerts(&srv, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_VERIFY_NONE, + certs, 0, NULL) == 0; + + if (ok) { + mbedtls_ssl_conf_verify(&cli.conf, VerifyCb, v); + } + return ok; +} + +/* From the root in the client's CA list down to the server's certificate, + * each with the certificates the server sent after it */ +static int VerifyTopDown(void) +{ + Verify v; + int s; + int ok; + + VerifyInit(&v); + v.want[0] = &chain; + v.want[1] = chain.next; + v.want[2] = chain.next->next; + v.want[3] = &root; + ok = VerifyPeers(MBEDTLS_SSL_VERIFY_REQUIRED, &chainCerts, HOST_NAME, + &v) && + Handshake(&s) == 0 && s == 0 && TopDown(&v, 4) && v.same && + (v.flags[0] | v.flags[1] | v.flags[2] | v.flags[3]) == 0 && + mbedtls_ssl_get_verify_result(&cli.ssl) == 0; + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* The flags of the untrusted top of the chain, cleared */ +static int VerifyClear(void) +{ + Verify v; + int s; + int ok; + + VerifyInit(&v); + v.clear = MBEDTLS_X509_BADCERT_NOT_TRUSTED; + v.want[0] = &chain; + v.want[1] = chain.next; + v.want[2] = chain.next->next; + ok = VerifyPeers(MBEDTLS_SSL_VERIFY_REQUIRED, &untrustedCerts, HOST_NAME, + &v) && + Handshake(&s) == 0 && s == 0 && TopDown(&v, 3) && v.same && + v.flags[0] == MBEDTLS_X509_BADCERT_NOT_TRUSTED && + (v.flags[1] | v.flags[2]) == 0 && + mbedtls_ssl_get_verify_result(&cli.ssl) == 0; + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* A name mismatch only, cleared, as for curl --no-verifyhost */ +static int VerifyClearHost(void) +{ + Verify v; + int s; + int ok; + + VerifyInit(&v); + v.clear = MBEDTLS_X509_BADCERT_CN_MISMATCH; + v.want[0] = &cert; + v.want[1] = &ca; + ok = VerifyPeers(MBEDTLS_SSL_VERIFY_REQUIRED, &defaultCerts, + "wrong.example.org", &v) && + Handshake(&s) == 0 && s == 0 && TopDown(&v, 2) && v.same && + v.flags[0] == 0 && v.flags[1] == MBEDTLS_X509_BADCERT_CN_MISMATCH && + mbedtls_ssl_get_verify_result(&cli.ssl) == 0; + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* The extended key usage of a client certificate is checked after the + * server's callback, which does not see it */ +static int VerifyUsage(void) +{ + Verify v; + int s = 0; + int ok; + + VerifyInit(&v); + v.clear = 0xFFFFFFFF; + ok = PeerNew(&cli, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_VERIFY_REQUIRED, 1, + HOST_NAME) == 0 && + mbedtls_ssl_conf_own_cert(&cli.conf, &ekuSrv, &key) == 0 && + mbedtls_ssl_session_reset(&cli.ssl) == 0 && + PeerNew(&srv, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_VERIFY_REQUIRED, 0, + NULL) == 0; + if (ok) { + mbedtls_ssl_conf_verify(&srv.conf, VerifyCb, &v); + (void)Handshake(&s); + ok = s == MBEDTLS_ERR_SSL_BAD_CERTIFICATE && TopDown(&v, 2) && + (v.flags[0] | v.flags[1]) == 0 && + mbedtls_ssl_get_verify_result(&srv.ssl) == + MBEDTLS_X509_BADCERT_EXT_KEY_USAGE; + } + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* A flag set on a good chain fails the handshake */ +static int VerifySet(void) +{ + Verify v; + int s; + int ok; + + VerifyInit(&v); + v.set = MBEDTLS_X509_BADCERT_OTHER; + ok = VerifyPeers(MBEDTLS_SSL_VERIFY_REQUIRED, &chainCerts, HOST_NAME, + &v) && + Handshake(&s) == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED && + TopDown(&v, 4) && + mbedtls_ssl_get_verify_result(&cli.ssl) == MBEDTLS_X509_BADCERT_OTHER; + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* A callback that fails stops the handshake, also with VERIFY_OPTIONAL */ +static int VerifyFails(int cbRet, int want) +{ + Verify v; + int s; + int ok; + + VerifyInit(&v); + v.ret = cbRet; + ok = VerifyPeers(MBEDTLS_SSL_VERIFY_OPTIONAL, &chainCerts, HOST_NAME, + &v) && + Handshake(&s) == want && v.calls == 1 && + mbedtls_ssl_get_verify_result(&cli.ssl) == 0xFFFFFFFF; + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +static int VerifyError(void) +{ + return VerifyFails(VERIFY_CB_ERR, VERIFY_CB_ERR); +} + +static int VerifyErrorVerifyFailed(void) +{ + return VerifyFails(MBEDTLS_ERR_X509_CERT_VERIFY_FAILED, + MBEDTLS_ERR_X509_FATAL_ERROR); +} + +/* The callback set on the context is used in place of the config's */ +static int VerifyContext(void) +{ + Verify vConf; + Verify v; + int s; + int ok; + + VerifyInit(&vConf); + VerifyInit(&v); + ok = VerifyPeers(MBEDTLS_SSL_VERIFY_REQUIRED, &chainCerts, HOST_NAME, + &vConf); + if (ok) { + mbedtls_ssl_set_verify(&cli.ssl, VerifyCb, &v); + ok = Handshake(&s) == 0 && vConf.calls == 0 && TopDown(&v, 4); + } + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* After a handshake that accepted the untrusted CAs of the chain, or + * rejected them, the next handshake on the config does not trust them */ +static int VerifyNoCaCache(int authmode, uint32_t clear, int want) +{ + const Certs leafCerts = { &ca, &leaf, &leafKey }; + Verify v; + int s; + int ok; + + VerifyInit(&v); + v.clear = clear; + ok = VerifyPeers(authmode, &untrustedCerts, HOST_NAME, &v) && + Handshake(&s) == want; + PeerFree(&srv); + if (ok) { + VerifyInit(&v); + memset(cli.tx, 0, sizeof(*cli.tx)); + ok = mbedtls_ssl_session_reset(&cli.ssl) == 0 && + PeerNewCerts(&srv, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_VERIFY_NONE, + &leafCerts, 0, NULL) == 0; + } + if (ok) { + (void)Handshake(&s); + ok = (mbedtls_ssl_get_verify_result(&cli.ssl) & + MBEDTLS_X509_BADCERT_NOT_TRUSTED) != 0; + } + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +static int NoCaCacheOptional(void) +{ + return VerifyNoCaCache(MBEDTLS_SSL_VERIFY_OPTIONAL, 0, 0); +} + +static int NoCaCacheCleared(void) +{ + return VerifyNoCaCache(MBEDTLS_SSL_VERIFY_REQUIRED, + MBEDTLS_X509_BADCERT_NOT_TRUSTED, 0); +} + +static int NoCaCacheRejected(void) +{ + return VerifyNoCaCache(MBEDTLS_SSL_VERIFY_REQUIRED, 0, + MBEDTLS_ERR_X509_CERT_VERIFY_FAILED); +} + +static int ReadWrite(void) +{ + unsigned char buf[16]; + int ok = Connect(MBEDTLS_SSL_VERIFY_NONE) == 0 && + mbedtls_ssl_write(&cli.ssl, (const unsigned char*)"hello", 5) == 5 && + mbedtls_ssl_read(&srv.ssl, buf, sizeof(buf)) == 5 && + memcmp(buf, "hello", 5) == 0; + + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +static int WriteZero(void) +{ + int ok = Connect(MBEDTLS_SSL_VERIFY_NONE) == 0 && + mbedtls_ssl_write(&cli.ssl, (const unsigned char*)"", 0) == 0; + + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* The transport ends without close_notify */ +static int ReadEof(void) +{ + unsigned char buf[16]; + int ok = Connect(MBEDTLS_SSL_VERIFY_NONE) == 0; + + if (ok) { + toClient.closed = 1; + ok = mbedtls_ssl_read(&cli.ssl, buf, sizeof(buf)) == 0; + } + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +static int ReadCloseNotify(void) +{ + unsigned char buf[16]; + int ok = Connect(MBEDTLS_SSL_VERIFY_NONE) == 0 && + mbedtls_ssl_close_notify(&srv.ssl) == 0 && + mbedtls_ssl_read(&cli.ssl, buf, sizeof(buf)) == + MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY; + + PeerFree(&cli); + PeerFree(&srv); + return ok; +} + +/* A conf_* failure after mbedtls_ssl_setup */ +static int ResetConfError(void) +{ + /* Not read: any CRL is refused */ + mbedtls_x509_crl* crl = (mbedtls_x509_crl*)&ca; + int ok = PeerNew(&srv, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_VERIFY_NONE, 0, + NULL) == 0; + + if (ok) { + mbedtls_ssl_conf_ca_chain(&srv.conf, &ca, crl); + ok = mbedtls_ssl_session_reset(&srv.ssl) == + MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE; + } + PeerFree(&srv); + return ok; +} + +/* wolfSSL's own code for a key it refuses is not returned */ +static int OwnKeyTooSmall(void) +{ + mbedtls_pk_context small; + int ok; + + mbedtls_pk_init(&small); + mbedtls_ssl_config_init(&srv.conf); + ok = mbedtls_pk_parse_keyfile(&small, "rsa_pkcs1_768_clear.der", + NULL) == 0 && + mbedtls_ssl_config_defaults(&srv.conf, MBEDTLS_SSL_IS_SERVER, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) == 0 && + mbedtls_ssl_conf_own_cert(&srv.conf, &cert, &small) == + MBEDTLS_ERR_SSL_BAD_INPUT_DATA; + mbedtls_ssl_config_free(&srv.conf); + mbedtls_pk_free(&small); + return ok; +} + +static int AcceptWouldBlock(void) +{ + mbedtls_net_context bound; + mbedtls_net_context client; + int ok; + + mbedtls_net_init(&bound); + mbedtls_net_init(&client); + ok = mbedtls_net_bind(&bound, "127.0.0.1", "0", + MBEDTLS_NET_PROTO_TCP) == 0 && + fcntl(bound.fd, F_SETFL, fcntl(bound.fd, F_GETFL) | O_NONBLOCK) == + 0 && + mbedtls_net_accept(&bound, &client, NULL, 0, NULL) == + MBEDTLS_ERR_SSL_WANT_READ; + mbedtls_net_free(&client); + mbedtls_net_free(&bound); + return ok; +} + +static const struct { + const char* name; + int (*test)(void); +} cases[] = { + { "no client certificate, OPTIONAL", NoClientCertOptional }, + { "no client certificate, REQUIRED", NoClientCertRequired }, + { "host name not set", HostNotSet }, + { "host name cleared", HostCleared }, + { "host name replaced", HostReplaced }, + { "wrong host name", HostWrong }, + { "verify callback, top down", VerifyTopDown }, + { "verify callback clears NOT_TRUSTED", VerifyClear }, + { "verify callback clears CN_MISMATCH", VerifyClearHost }, + { "verify callback cannot clear EXT_KEY_USAGE", VerifyUsage }, + { "verify callback sets a flag", VerifySet }, + { "verify callback error", VerifyError }, + { "verify callback returns CERT_VERIFY_FAILED", VerifyErrorVerifyFailed }, + { "verify callback on the context", VerifyContext }, + { "untrusted chain CA not kept, OPTIONAL", NoCaCacheOptional }, + { "untrusted chain CA not kept, cleared", NoCaCacheCleared }, + { "untrusted chain CA not kept, rejected", NoCaCacheRejected }, + { "write and read", ReadWrite }, + { "zero-length write", WriteZero }, + { "EOF without close_notify", ReadEof }, + { "close_notify", ReadCloseNotify }, + { "session reset after a conf error", ResetConfError }, + { "own key too small", OwnKeyTooSmall }, + { "accept would block", AcceptWouldBlock } +}; + +int main(void) +{ + size_t i; + int fails = 0; + + mbedtls_x509_crt_init(&ca); + mbedtls_x509_crt_init(&cert); + mbedtls_pk_init(&key); + mbedtls_x509_crt_init(&root); + mbedtls_x509_crt_init(&chain); + mbedtls_x509_crt_init(&leaf); + mbedtls_pk_init(&leafKey); + mbedtls_x509_crt_init(&ekuSrv); + if (psa_crypto_init() != PSA_SUCCESS || + mbedtls_x509_crt_parse_file(&ca, CA_FILE) != 0 || + mbedtls_x509_crt_parse_file(&cert, CERT_FILE) != 0 || + mbedtls_pk_parse_keyfile(&key, KEY_FILE, NULL) != 0 || + mbedtls_x509_crt_parse_file(&root, ROOT_FILE) != 0 || + mbedtls_x509_crt_parse_file(&chain, CHAIN_FILE) != 0 || + mbedtls_x509_crt_parse_file(&leaf, LEAF_FILE) != 0 || + mbedtls_pk_parse_keyfile(&leafKey, LEAF_KEY_FILE, NULL) != 0 || + mbedtls_x509_crt_parse_file(&ekuSrv, EKU_SRV_FILE) != 0) { + printf("FAIL: loading the certificates and keys\n"); + return 1; + } + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + if (cases[i].test()) { + printf("PASS: %s\n", cases[i].name); + } + else { + printf("FAIL: %s\n", cases[i].name); + fails++; + } + } + mbedtls_x509_crt_free(&ekuSrv); + mbedtls_pk_free(&leafKey); + mbedtls_x509_crt_free(&leaf); + mbedtls_x509_crt_free(&chain); + mbedtls_x509_crt_free(&root); + mbedtls_pk_free(&key); + mbedtls_x509_crt_free(&cert); + mbedtls_x509_crt_free(&ca); + mbedtls_psa_crypto_free(); + return (fails == 0) ? 0 : 1; +} diff --git a/test/mbedtls_compat/x509_crt_test.c b/test/mbedtls_compat/x509_crt_test.c new file mode 100644 index 00000000..ca96c212 --- /dev/null +++ b/test/mbedtls_compat/x509_crt_test.c @@ -0,0 +1,157 @@ +/* x509_crt_test.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Certificate parsing through the Mbed TLS 4.x API, one node per + * certificate. The cases of mbedtls_x509_crt_parse_file come from Mbed TLS's + * tests/suites/test_suite_x509parse.data. Run from framework/data_files in + * an Mbed TLS checkout. */ + +#include +#include + +#include +#include + +/* Any error code */ +#define ERR -1 +#define IN "parse_input/" + +static const struct { + const char* file; + int ret; /* number that failed, or ERR */ + int certs; +} cases[] = { + { IN "server5.basic-constraints-integer-first.badsign.crt", 0, 1 }, + { IN "server5.basic-constraints-integer-first.selfsigned.crt", 0, 1 }, + { IN "server1_pathlen_int_max.crt", ERR, 0 }, + { "dir3/Readme", ERR, 0 }, + { "dir3/test-ca2.crt", 0, 1 }, + { IN "server7_int-ca.crt", 0, 2 }, + { IN "server7_trailing_space.crt", 0, 2 }, + { IN "cli-rsa-sha256-badalg.crt.der", ERR, 0 }, + /* wolfSSL ignores bytes after an empty basicConstraints. Mbed TLS: ERR */ + { IN "server5.basic-constraints-sequence-overflow.badsign.crt", 0, 1 }, + { IN "server5.basic-constraints-sequence-overflow.selfsigned.crt", 0, 1 }, + /* Over WOLFSSL_MAX_PATH_LEN, and serial number 0. Mbed TLS: 0, 1 */ + { IN "server1_pathlen_int_max-1.crt", ERR, 0 }, + { "dir3/test-ca.crt", ERR, 0 }, + /* wolfSSL skips spaces in base64. Mbed TLS: 1, 1 and ERR, 0 */ + { IN "server7_pem_space.crt", 0, 2 }, + { IN "server7_all_space.crt", 0, 2 }, + /* Not in Mbed TLS's cases */ + { "test-ca.der", 0, 1 }, + { "missing.crt", ERR, 0 }, + /* NULL parameters after the ECDSA signature OID */ + { "test-ca2.crt", 0, 1 } +}; + +/* -1 for an empty node, apart from the first when no certificate parsed */ +static int CountCerts(const mbedtls_x509_crt* chain) +{ + const mbedtls_x509_crt* cur; + int certs = 0; + int nodes = 0; + + for (cur = chain; cur != NULL; cur = cur->next) { + nodes++; + if (cur->raw.p != NULL) { + certs++; + } + } + return (certs == nodes || (certs == 0 && nodes == 1)) ? certs : -1; +} + +/* Mbed TLS ignores data after a DER certificate, such as a NUL */ +static int TrailingByte(void) +{ + mbedtls_x509_crt crt; + mbedtls_x509_crt copy; + unsigned char buf[2048]; + int ok = 0; + + mbedtls_x509_crt_init(&crt); + mbedtls_x509_crt_init(©); + if (mbedtls_x509_crt_parse_file(&crt, "test-ca.der") == 0 && + crt.raw.len < sizeof(buf)) { + memcpy(buf, crt.raw.p, crt.raw.len); + buf[crt.raw.len] = 0; + ok = mbedtls_x509_crt_parse_der(©, buf, crt.raw.len + 1) == 0 && + copy.raw.len == crt.raw.len; + } + mbedtls_x509_crt_free(©); + mbedtls_x509_crt_free(&crt); + return ok; +} + +int main(void) +{ + mbedtls_x509_crt crt; + mbedtls_x509_crt all; + size_t i; + int total = 0; + int fails = 0; + int certs; + int ret; + + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + /* Every file also goes into one chain, which only grows */ + mbedtls_x509_crt_init(&all); + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + mbedtls_x509_crt_init(&crt); + ret = mbedtls_x509_crt_parse_file(&crt, cases[i].file); + certs = CountCerts(&crt); + if ((cases[i].ret == ERR ? ret < 0 : ret == cases[i].ret) && + certs == cases[i].certs) { + printf("PASS: %s\n", cases[i].file); + } + else { + printf("FAIL: %s: returned %d with %d certificates, expected %d " + "with %d\n", cases[i].file, ret, certs, cases[i].ret, + cases[i].certs); + fails++; + } + mbedtls_x509_crt_free(&crt); + (void)mbedtls_x509_crt_parse_file(&all, cases[i].file); + total += cases[i].certs; + } + certs = CountCerts(&all); + if (certs == total) { + printf("PASS: one chain\n"); + } + else { + printf("FAIL: one chain: %d certificates, expected %d\n", certs, + total); + fails++; + } + if (TrailingByte()) { + printf("PASS: DER with a trailing byte\n"); + } + else { + printf("FAIL: DER with a trailing byte\n"); + fails++; + } + mbedtls_x509_crt_free(&all); + mbedtls_psa_crypto_free(); + return (fails == 0) ? 0 : 1; +} diff --git a/user_settings.h b/user_settings.h index d6fa343a..aaaa7a4f 100644 --- a/user_settings.h +++ b/user_settings.h @@ -34,7 +34,9 @@ #include_next "user_settings.h" #else +#ifndef WOLFPSA_MBEDTLS_COMPAT #define WOLFCRYPT_ONLY +#endif /* WOLFPSA_MBEDTLS_COMPAT */ /* MBEDTLS_THREADING_C: thread safe, see src/psa_lock.h */ #if defined(MBEDTLS_THREADING_C) && !defined(WOLFPSA_THREAD_SAFE) #define WOLFPSA_THREAD_SAFE @@ -137,6 +139,30 @@ #define HAVE_XCHACHA #define HAVE_CMAC_KDF +/* wolfSSL TLS for the Mbed TLS compatibility layer (MBEDTLS_COMPAT=1), with + * configure's TLS defaults. NO_DH: wolfPSA builds no dh.c. */ +#ifdef WOLFPSA_MBEDTLS_COMPAT +#define HAVE_TLS_EXTENSIONS +#define HAVE_SNI +#define HAVE_SUPPORTED_CURVES +#define HAVE_EXTENDED_MASTER +#define HAVE_ENCRYPT_THEN_MAC +#define HAVE_SERVER_RENEGOTIATION_INFO +#define WOLFSSL_TLS13 +#define NO_OLD_TLS +#define NO_DES3_TLS_SUITES +#define NO_PSK +#define NO_DH +/* Mbed TLS accepts NULL parameters after an ECDSA signature OID */ +#define WOLFSSL_ECC_SIGALG_PARAMS_NULL_ALLOWED +/* For the verify callback, see MbedVerifyCb() in src/mbedtls_compat.c */ +#define WOLFSSL_ALWAYS_VERIFY_CB +/* mbedtls_ssl_conf_ciphersuites() takes cipher suite IDs */ +#define WOLFSSL_SET_CIPHER_BYTES +/* An IP address host name is checked against the certificate's IP SANs */ +#define WOLFSSL_IP_ALT_NAME +#endif /* WOLFPSA_MBEDTLS_COMPAT */ + #endif /* __ZEPHYR__ */ #endif /* WOLFSSL_USER_SETTINGS_H */ diff --git a/wolfpsa.map b/wolfpsa.map index af832720..fb6d7026 100644 --- a/wolfpsa.map +++ b/wolfpsa.map @@ -139,6 +139,10 @@ WOLFPSA_1.0 { wolfpsa_test_set_next_key_id; wolfPSA_SetDefaultDevID; wolfPSA_GetDefaultDevID; + /* Mbed TLS compatibility layer, MBEDTLS_COMPAT=1 */ + mbedtls_net_*; + mbedtls_ssl_*; + mbedtls_x509_*; /* The wolfCrypt objects a devId selects live inside this library, * so a shared-library user has to register the device against this * copy of the callback table rather than their own wolfSSL. The raw diff --git a/wolfpsa/mbedtls/asn1.h b/wolfpsa/mbedtls/asn1.h new file mode 100644 index 00000000..b7aa67d2 --- /dev/null +++ b/wolfpsa/mbedtls/asn1.h @@ -0,0 +1,35 @@ +/* asn1.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef MBEDTLS_ASN1_H +#define MBEDTLS_ASN1_H + +#include "tf-psa-crypto/build_info.h" + +#include + +typedef struct mbedtls_asn1_buf { + int tag; + size_t len; + unsigned char* p; +} mbedtls_asn1_buf; + +#endif /* MBEDTLS_ASN1_H */ diff --git a/wolfpsa/mbedtls/build_info.h b/wolfpsa/mbedtls/build_info.h new file mode 100644 index 00000000..d5bc4f53 --- /dev/null +++ b/wolfpsa/mbedtls/build_info.h @@ -0,0 +1,50 @@ +/* build_info.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Mbed TLS feature macros for the compatibility layer, built with + * MBEDTLS_COMPAT=1. */ + +#ifndef MBEDTLS_BUILD_INFO_H +#define MBEDTLS_BUILD_INFO_H + +#include "tf-psa-crypto/build_info.h" + +/* The Mbed TLS API version this layer follows */ +#define MBEDTLS_VERSION_MAJOR 4 +#define MBEDTLS_VERSION_MINOR 2 +#define MBEDTLS_VERSION_PATCH 0 +#define MBEDTLS_VERSION_NUMBER 0x04020000 +#define MBEDTLS_VERSION_STRING "4.2.0" +#define MBEDTLS_VERSION_STRING_FULL "Mbed TLS 4.2.0" + +#define MBEDTLS_NET_C +#define MBEDTLS_SSL_TLS_C +#define MBEDTLS_SSL_CLI_C +#define MBEDTLS_SSL_SRV_C +#define MBEDTLS_SSL_PROTO_TLS1_2 +#define MBEDTLS_SSL_PROTO_TLS1_3 +#define MBEDTLS_X509_CRT_PARSE_C +/* PEM certificates only, no mbedtls/pem.h */ +#define MBEDTLS_PEM_PARSE_C +/* No mbedtls_x509_crt_info() and friends yet */ +#define MBEDTLS_X509_REMOVE_INFO + +#endif /* MBEDTLS_BUILD_INFO_H */ diff --git a/wolfpsa/mbedtls/debug.h b/wolfpsa/mbedtls/debug.h new file mode 100644 index 00000000..eaa0c285 --- /dev/null +++ b/wolfpsa/mbedtls/debug.h @@ -0,0 +1,29 @@ +/* debug.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* MBEDTLS_DEBUG_C is not defined; mbedtls_ssl_conf_dbg is in ssl.h. */ + +#ifndef MBEDTLS_DEBUG_H +#define MBEDTLS_DEBUG_H + +#include "mbedtls/ssl.h" + +#endif /* MBEDTLS_DEBUG_H */ diff --git a/wolfpsa/mbedtls/error.h b/wolfpsa/mbedtls/error.h new file mode 100644 index 00000000..b1f33ba8 --- /dev/null +++ b/wolfpsa/mbedtls/error.h @@ -0,0 +1,29 @@ +/* error.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* No mbedtls_strerror() yet. */ + +#ifndef MBEDTLS_ERROR_H +#define MBEDTLS_ERROR_H + +#include "mbedtls/build_info.h" + +#endif /* MBEDTLS_ERROR_H */ diff --git a/wolfpsa/mbedtls/net_sockets.h b/wolfpsa/mbedtls/net_sockets.h new file mode 100644 index 00000000..b4131ead --- /dev/null +++ b/wolfpsa/mbedtls/net_sockets.h @@ -0,0 +1,72 @@ +/* net_sockets.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef MBEDTLS_NET_SOCKETS_H +#define MBEDTLS_NET_SOCKETS_H + +#include "mbedtls/ssl.h" + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +#define MBEDTLS_NET_PROTO_TCP 0 +#define MBEDTLS_NET_PROTO_UDP 1 + +#define MBEDTLS_ERR_NET_SOCKET_FAILED -0x0042 +#define MBEDTLS_ERR_NET_CONNECT_FAILED -0x0044 +#define MBEDTLS_ERR_NET_INVALID_CONTEXT -0x0045 +#define MBEDTLS_ERR_NET_BIND_FAILED -0x0046 +#define MBEDTLS_ERR_NET_LISTEN_FAILED -0x0048 +#define MBEDTLS_ERR_NET_ACCEPT_FAILED -0x004A +#define MBEDTLS_ERR_NET_RECV_FAILED -0x004C +#define MBEDTLS_ERR_NET_SEND_FAILED -0x004E +#define MBEDTLS_ERR_NET_CONN_RESET -0x0050 +#define MBEDTLS_ERR_NET_UNKNOWN_HOST -0x0052 +#define MBEDTLS_ERR_NET_BUFFER_TOO_SMALL PSA_ERROR_BUFFER_TOO_SMALL + +#define MBEDTLS_NET_LISTEN_BACKLOG 10 + +typedef struct mbedtls_net_context { + int fd; +} mbedtls_net_context; + +void mbedtls_net_init(mbedtls_net_context* ctx); +int mbedtls_net_connect(mbedtls_net_context* ctx, const char* host, + const char* port, int proto); +int mbedtls_net_bind(mbedtls_net_context* ctx, const char* bind_ip, + const char* port, int proto); +int mbedtls_net_accept(mbedtls_net_context* bind_ctx, + mbedtls_net_context* client_ctx, void* client_ip, size_t buf_size, + size_t* cip_len); +void mbedtls_net_usleep(unsigned long usec); +void mbedtls_net_close(mbedtls_net_context* ctx); +void mbedtls_net_free(mbedtls_net_context* ctx); +int mbedtls_net_recv(void* ctx, unsigned char* buf, size_t len); +int mbedtls_net_send(void* ctx, const unsigned char* buf, size_t len); + +#ifdef __cplusplus + } /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MBEDTLS_NET_SOCKETS_H */ diff --git a/wolfpsa/mbedtls/psa_util.h b/wolfpsa/mbedtls/psa_util.h new file mode 100644 index 00000000..f30228aa --- /dev/null +++ b/wolfpsa/mbedtls/psa_util.h @@ -0,0 +1,30 @@ +/* psa_util.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* No mbedtls_psa_get_random() or ECDSA signature conversion yet. */ + +#ifndef MBEDTLS_PSA_UTIL_H +#define MBEDTLS_PSA_UTIL_H + +#include "tf-psa-crypto/build_info.h" +#include "psa/crypto.h" + +#endif /* MBEDTLS_PSA_UTIL_H */ diff --git a/wolfpsa/mbedtls/ssl.h b/wolfpsa/mbedtls/ssl.h new file mode 100644 index 00000000..e2107d85 --- /dev/null +++ b/wolfpsa/mbedtls/ssl.h @@ -0,0 +1,184 @@ +/* ssl.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef MBEDTLS_SSL_H +#define MBEDTLS_SSL_H + +#include "mbedtls/build_info.h" +#include "mbedtls/x509_crt.h" +/* From wolfPSA, as Mbed TLS takes it from TF-PSA-Crypto */ +#include "psa/crypto.h" +/* Mbed TLS includes it through ssl_cache.h, on by default there */ +#include "mbedtls/threading.h" + +#include +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +#define MBEDTLS_SSL_IS_CLIENT 0 +#define MBEDTLS_SSL_IS_SERVER 1 +#define MBEDTLS_SSL_TRANSPORT_STREAM 0 +#define MBEDTLS_SSL_TRANSPORT_DATAGRAM 1 +#define MBEDTLS_SSL_PRESET_DEFAULT 0 +#define MBEDTLS_SSL_VERIFY_NONE 0 +#define MBEDTLS_SSL_VERIFY_OPTIONAL 1 +#define MBEDTLS_SSL_VERIFY_REQUIRED 2 +#define MBEDTLS_SSL_MAX_HOST_NAME_LEN 255 + +/* Mbed TLS defines -135 and -141 as PSA_ERROR_INVALID_ARGUMENT and + * PSA_ERROR_INSUFFICIENT_MEMORY */ +#define MBEDTLS_ERR_SSL_CERTIFICATE_VERIFICATION_WITHOUT_HOSTNAME -0x5D80 +#define MBEDTLS_ERR_SSL_TIMEOUT -0x6800 +#define MBEDTLS_ERR_SSL_WANT_WRITE -0x6880 +#define MBEDTLS_ERR_SSL_WANT_READ -0x6900 +#define MBEDTLS_ERR_SSL_INTERNAL_ERROR -0x6C00 +#define MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE -0x7080 +#define MBEDTLS_ERR_SSL_CONN_EOF -0x7280 +#define MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE -0x7480 +#define MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY -0x7880 +#define MBEDTLS_ERR_SSL_BAD_CERTIFICATE -0x7A00 +#define MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET -0x7B00 +#define MBEDTLS_ERR_SSL_HW_ACCEL_FAILED -0x7F80 +#define MBEDTLS_ERR_SSL_BAD_INPUT_DATA -135 +#define MBEDTLS_ERR_SSL_ALLOC_FAILED -141 + +typedef enum { + MBEDTLS_SSL_VERSION_UNKNOWN, + MBEDTLS_SSL_VERSION_TLS1_2 = 0x0303, + MBEDTLS_SSL_VERSION_TLS1_3 = 0x0304 +} mbedtls_ssl_protocol_version; + +/* wolfSSL objects, opaque to the application */ +struct WOLFSSL_CTX; +struct WOLFSSL; +struct WOLFSSL_SESSION; +/* The peer's chain while it is verified, in src/mbedtls_compat.c */ +struct MbedChain; + +typedef int mbedtls_ssl_send_t(void* ctx, const unsigned char* buf, + size_t len); +typedef int mbedtls_ssl_recv_t(void* ctx, unsigned char* buf, size_t len); +typedef int mbedtls_ssl_recv_timeout_t(void* ctx, unsigned char* buf, + size_t len, uint32_t timeout); + +typedef struct mbedtls_ssl_config { + struct WOLFSSL_CTX* ctx; + int err; /* first failed void conf call, returned at setup and reset */ + int authmode; + int endpoint; + mbedtls_ssl_protocol_version min_tls_version; + mbedtls_ssl_protocol_version max_tls_version; + const int* ciphersuites; /* the application's, NULL for the default */ + mbedtls_x509_crt* ca_chain; /* the application's */ + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*); + void* p_vrfy; +} mbedtls_ssl_config; + +typedef struct mbedtls_ssl_session { + struct WOLFSSL_SESSION* session; +} mbedtls_ssl_session; + +typedef struct mbedtls_ssl_context { + const mbedtls_ssl_config* conf; + struct WOLFSSL* ssl; + char* hostname; /* applied again by mbedtls_ssl_session_reset() */ + int hostname_set; /* mbedtls_ssl_set_hostname() was called */ + void* p_bio; + mbedtls_ssl_send_t* f_send; + mbedtls_ssl_recv_t* f_recv; + mbedtls_ssl_recv_timeout_t* f_recv_timeout; + /* Used in place of the config's when set */ + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*); + void* p_vrfy; + int authmode; + uint32_t verify_flags; /* MBEDTLS_X509_BADCERT_* from the handshake */ + int verify_ret; /* the verify callback's error */ + int peer_cert; /* the peer sent a certificate */ + struct MbedChain* chain; +} mbedtls_ssl_context; + +void mbedtls_ssl_config_init(mbedtls_ssl_config* conf); +void mbedtls_ssl_config_free(mbedtls_ssl_config* conf); +int mbedtls_ssl_config_defaults(mbedtls_ssl_config* conf, + int endpoint, int transport, int preset); +void mbedtls_ssl_conf_authmode(mbedtls_ssl_config* conf, int authmode); +void mbedtls_ssl_conf_ca_chain(mbedtls_ssl_config* conf, + mbedtls_x509_crt* ca_chain, mbedtls_x509_crl* ca_crl); +int mbedtls_ssl_conf_own_cert(mbedtls_ssl_config* conf, + mbedtls_x509_crt* own_cert, mbedtls_pk_context* pk_key); +void mbedtls_ssl_conf_dbg(mbedtls_ssl_config* conf, + void (*f_dbg)(void*, int, const char*, int, const char*), void* p_dbg); +void mbedtls_ssl_conf_min_tls_version(mbedtls_ssl_config* conf, + mbedtls_ssl_protocol_version tls_version); +void mbedtls_ssl_conf_max_tls_version(mbedtls_ssl_config* conf, + mbedtls_ssl_protocol_version tls_version); +void mbedtls_ssl_conf_ciphersuites(mbedtls_ssl_config* conf, + const int* ciphersuites); +void mbedtls_ssl_conf_verify(mbedtls_ssl_config* conf, + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*), void* p_vrfy); +void mbedtls_ssl_conf_cert_profile(mbedtls_ssl_config* conf, + const mbedtls_x509_crt_profile* profile); + +const int* mbedtls_ssl_list_ciphersuites(void); +const char* mbedtls_ssl_get_ciphersuite_name(const int ciphersuite_id); + +void mbedtls_ssl_init(mbedtls_ssl_context* ssl); +void mbedtls_ssl_free(mbedtls_ssl_context* ssl); +int mbedtls_ssl_setup(mbedtls_ssl_context* ssl, const mbedtls_ssl_config* conf); +int mbedtls_ssl_session_reset(mbedtls_ssl_context* ssl); +void mbedtls_ssl_set_bio(mbedtls_ssl_context* ssl, void* p_bio, + mbedtls_ssl_send_t* f_send, mbedtls_ssl_recv_t* f_recv, + mbedtls_ssl_recv_timeout_t* f_recv_timeout); +void mbedtls_ssl_set_verify(mbedtls_ssl_context* ssl, + int (*f_vrfy)(void*, mbedtls_x509_crt*, int, uint32_t*), void* p_vrfy); +int mbedtls_ssl_set_hostname(mbedtls_ssl_context* ssl, const char* hostname); +int mbedtls_ssl_handshake(mbedtls_ssl_context* ssl); +uint32_t mbedtls_ssl_get_verify_result(const mbedtls_ssl_context* ssl); +const char* mbedtls_ssl_get_ciphersuite(const mbedtls_ssl_context* ssl); +int mbedtls_ssl_get_ciphersuite_id_from_ssl(const mbedtls_ssl_context* ssl); +const char* mbedtls_ssl_get_version(const mbedtls_ssl_context* ssl); +mbedtls_ssl_protocol_version mbedtls_ssl_get_version_number( + const mbedtls_ssl_context* ssl); +size_t mbedtls_ssl_get_bytes_avail(const mbedtls_ssl_context* ssl); +int mbedtls_ssl_read(mbedtls_ssl_context* ssl, unsigned char* buf, size_t len); +int mbedtls_ssl_write(mbedtls_ssl_context* ssl, + const unsigned char* buf, size_t len); +int mbedtls_ssl_close_notify(mbedtls_ssl_context* ssl); + +void mbedtls_ssl_session_init(mbedtls_ssl_session* session); +void mbedtls_ssl_session_free(mbedtls_ssl_session* session); +int mbedtls_ssl_get_session(const mbedtls_ssl_context* ssl, + mbedtls_ssl_session* session); +int mbedtls_ssl_set_session(mbedtls_ssl_context* ssl, + const mbedtls_ssl_session* session); +int mbedtls_ssl_session_save(const mbedtls_ssl_session* session, + unsigned char* buf, size_t buf_len, size_t* olen); +int mbedtls_ssl_session_load(mbedtls_ssl_session* session, + const unsigned char* buf, size_t len); + +#ifdef __cplusplus + } /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MBEDTLS_SSL_H */ diff --git a/wolfpsa/mbedtls/timing.h b/wolfpsa/mbedtls/timing.h new file mode 100644 index 00000000..5c212459 --- /dev/null +++ b/wolfpsa/mbedtls/timing.h @@ -0,0 +1,29 @@ +/* timing.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* No mbedtls_timing_* yet; DTLS needs them. */ + +#ifndef MBEDTLS_TIMING_H +#define MBEDTLS_TIMING_H + +#include "mbedtls/build_info.h" + +#endif /* MBEDTLS_TIMING_H */ diff --git a/wolfpsa/mbedtls/version.h b/wolfpsa/mbedtls/version.h new file mode 100644 index 00000000..d60952ef --- /dev/null +++ b/wolfpsa/mbedtls/version.h @@ -0,0 +1,30 @@ +/* version.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* The version macros are in build_info.h. MBEDTLS_VERSION_C is not defined, + * so there is no mbedtls_version_get_number() and friends. */ + +#ifndef MBEDTLS_VERSION_H +#define MBEDTLS_VERSION_H + +#include "mbedtls/build_info.h" + +#endif /* MBEDTLS_VERSION_H */ diff --git a/wolfpsa/mbedtls/x509.h b/wolfpsa/mbedtls/x509.h new file mode 100644 index 00000000..bcb7d5e0 --- /dev/null +++ b/wolfpsa/mbedtls/x509.h @@ -0,0 +1,60 @@ +/* x509.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef MBEDTLS_X509_H +#define MBEDTLS_X509_H + +#include "mbedtls/build_info.h" +#include "mbedtls/asn1.h" +#include "mbedtls/pk.h" + +typedef mbedtls_asn1_buf mbedtls_x509_buf; + +#define MBEDTLS_X509_BADCERT_EXPIRED 0x01 +#define MBEDTLS_X509_BADCERT_REVOKED 0x02 +#define MBEDTLS_X509_BADCERT_CN_MISMATCH 0x04 +#define MBEDTLS_X509_BADCERT_NOT_TRUSTED 0x08 +#define MBEDTLS_X509_BADCRL_NOT_TRUSTED 0x10 +#define MBEDTLS_X509_BADCRL_EXPIRED 0x20 +#define MBEDTLS_X509_BADCERT_MISSING 0x40 +#define MBEDTLS_X509_BADCERT_SKIP_VERIFY 0x80 +#define MBEDTLS_X509_BADCERT_OTHER 0x0100 +#define MBEDTLS_X509_BADCERT_FUTURE 0x0200 +#define MBEDTLS_X509_BADCRL_FUTURE 0x0400 +#define MBEDTLS_X509_BADCERT_KEY_USAGE 0x0800 +#define MBEDTLS_X509_BADCERT_EXT_KEY_USAGE 0x1000 +#define MBEDTLS_X509_BADCERT_NS_CERT_TYPE 0x2000 +#define MBEDTLS_X509_BADCERT_BAD_MD 0x4000 +#define MBEDTLS_X509_BADCERT_BAD_PK 0x8000 +#define MBEDTLS_X509_BADCERT_BAD_KEY 0x010000 +#define MBEDTLS_X509_BADCRL_BAD_MD 0x020000 +#define MBEDTLS_X509_BADCRL_BAD_PK 0x040000 +#define MBEDTLS_X509_BADCRL_BAD_KEY 0x080000 + +/* Mbed TLS defines -141 as PSA_ERROR_INSUFFICIENT_MEMORY */ +#define MBEDTLS_ERR_X509_INVALID_FORMAT -0x2180 +#define MBEDTLS_ERR_X509_CERT_VERIFY_FAILED -0x2700 +#define MBEDTLS_ERR_X509_BAD_INPUT_DATA -0x2800 +#define MBEDTLS_ERR_X509_FILE_IO_ERROR -0x2900 +#define MBEDTLS_ERR_X509_FATAL_ERROR -0x3000 +#define MBEDTLS_ERR_X509_ALLOC_FAILED -141 + +#endif /* MBEDTLS_X509_H */ diff --git a/wolfpsa/mbedtls/x509_crt.h b/wolfpsa/mbedtls/x509_crt.h new file mode 100644 index 00000000..aefebf9c --- /dev/null +++ b/wolfpsa/mbedtls/x509_crt.h @@ -0,0 +1,63 @@ +/* x509_crt.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfPSA. + * + * wolfPSA is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfPSA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef MBEDTLS_X509_CRT_H +#define MBEDTLS_X509_CRT_H + +#include "mbedtls/x509.h" + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/* Each node contains one certificate, DER */ +typedef struct mbedtls_x509_crt { + mbedtls_x509_buf raw; + struct mbedtls_x509_crt* next; +} mbedtls_x509_crt; + +typedef struct mbedtls_x509_crl mbedtls_x509_crl; + +/* Not applied: wolfSSL's own limits hold */ +typedef struct mbedtls_x509_crt_profile mbedtls_x509_crt_profile; + +extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_default; +extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_next; +extern const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_suiteb; + +void mbedtls_x509_crt_init(mbedtls_x509_crt* crt); +void mbedtls_x509_crt_free(mbedtls_x509_crt* crt); +int mbedtls_x509_crt_parse(mbedtls_x509_crt* chain, + const unsigned char* buf, size_t buflen); +int mbedtls_x509_crt_parse_der(mbedtls_x509_crt* chain, + const unsigned char* buf, size_t buflen); +#if defined(MBEDTLS_FS_IO) +int mbedtls_x509_crt_parse_file(mbedtls_x509_crt* chain, const char* path); +int mbedtls_x509_crt_parse_path(mbedtls_x509_crt* chain, const char* path); +#endif /* MBEDTLS_FS_IO */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MBEDTLS_X509_CRT_H */