diff --git a/.github/workflows/build-config-matrix.yml b/.github/workflows/build-config-matrix.yml index b1d015b..5009e7b 100644 --- a/.github/workflows/build-config-matrix.yml +++ b/.github/workflows/build-config-matrix.yml @@ -80,18 +80,27 @@ jobs: modifiers: "-HAVE_HKDF -HAVE_ECC_ENCRYPT" - name: tls-prf modifiers: "-WOLFSSL_HAVE_PRF" + # This config stays as a static library (not shared) because of a + # missing symbol in this build of wolfSSL. 'target' field and the + # LDFLAGS environment variable below can be removed after + # wolfSSL#11958 merges. - name: pbkdf2 modifiers: "-HAVE_PBKDF2 +NO_PBKDF2" + target: libwolfpsa.a - name: ecc384 modifiers: "-WOLFSSL_SP_384 -HAVE_ECC384" + - name: no-ecc + modifiers: "-HAVE_ECC -HAVE_ECC_ENCRYPT" - name: rsa-1024 modifiers: "-WOLFSSL_SP_1024 -RSA_MIN_SIZE +RSA_MIN_SIZE=2048" - # wolfPSA's own sources only: the bundled wolfCrypt sources do not - # build under WC_NO_RNG (asn.c trips -Wnonnull), which is a wolfSSL - # issue outside this lane's scope. + - name: no-rsa + modifiers: "+NO_RSA +ECC_USER_CURVES -WOLFSSL_HAVE_MLDSA" - name: no-rng modifiers: "+WC_NO_RNG +WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS" target: psa-objects + - name: no-filesystem + modifiers: "+NO_FILESYSTEM +WOLFPSA_CUSTOM_STORE" + target: libwolfpsa.a steps: - name: Check out wolfPSA @@ -107,5 +116,7 @@ jobs: - name: Build ${{ matrix.config.name }} env: - BUILD_TARGET: ${{ matrix.config.target || 'libwolfpsa.a' }} + BUILD_TARGET: ${{ matrix.config.target || 'libwolfpsa.so' }} + # Fail on undefined symbols + LDFLAGS: -Wl,-z,defs run: ./build-test/build-variant.sh "${{ matrix.config.name }}" ${{ matrix.config.modifiers }} diff --git a/.github/workflows/mbedtls-compat.yml b/.github/workflows/mbedtls-compat.yml new file mode 100644 index 0000000..491e296 --- /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/.github/workflows/test-psa-api.yml b/.github/workflows/test-psa-api.yml index f0aa934..753265e 100644 --- a/.github/workflows/test-psa-api.yml +++ b/.github/workflows/test-psa-api.yml @@ -65,6 +65,11 @@ jobs: ./test/psa_ecc_verify_curve_test ./test/psa_ecc_ecdh_curve_test + - name: Run the PSA_WANT surface test + run: | + make want-surface-test \ + USER_SETTINGS_PATH="${GITHUB_WORKSPACE}/build-ecc-var" + test-psa-api: runs-on: ubuntu-latest strategy: diff --git a/.gitignore b/.gitignore index 9542e25..0918f3c 100644 --- a/.gitignore +++ b/.gitignore @@ -34,12 +34,15 @@ test/psa_14_misc_test test/psa_aead_multipart_test test/psa_cipher_inplace_test test/psa_cipher_oneshot_len_test +test/psa_reset_outputs_test test/psa_cipher_overlap_test test/psa_copy_key_narrowing_test test/psa_des3_pkcs7_test +test/psa_aead_call_order_test test/psa_ecc_curve_caps_test test/psa_ecc_ecdh_curve_test test/psa_ecc_sig_len_test +test/psa_rsa_status_test test/psa_ecc_verify_curve_test test/psa_eddsa_mont_export_test test/psa_eddsa_mont_gen_test @@ -70,6 +73,8 @@ test/psa_devid_cryptocb_test test/build-probe test/build-cryptocb test/build-cryptocb-asan +test/build-enroll +test/build-enroll-asan test/psa_store_commit_test test/psa_store_dir_validation_test test/psa_store_read_open_test @@ -79,7 +84,17 @@ test/psa_kdf_repeat_step_test test/psa_kdf_psk_to_ms_size_test test/psa_kdf_zeroize_output_test test/psa_import_zero_length_test +test/psa_import_key_data_test test/psa_copy_key_cross_lifetime_test -build-test/out/ -test/psa_zero_capacity_buffer_test test/psa_tf_psa_extra_test +test/psa_want_surface_test +test/psa_tf_psa_pk_test +test/psa_tf_psa_threading_test +test/psa_aead_gcm_short_tag_test +test/build-threads +test/build-threads-asan +test/psa_zero_capacity_buffer_test +test/psa_enrollment_alg_test +build-test/out/ +.cache/ +compile_commands.json diff --git a/Makefile b/Makefile index 2e09f21..a9c63ed 100644 --- a/Makefile +++ b/Makefile @@ -79,10 +79,38 @@ WOLFCRYPT_SRC := \ WOLFCRYPT_OBJ := $(patsubst $(WOLFSSL_PATH)/wolfcrypt/src/%.c,$(OBJDIR)/wolfcrypt_%.o,$(WOLFCRYPT_SRC)) WOLFCRYPT_OBJ_PIC := $(patsubst $(WOLFSSL_PATH)/wolfcrypt/src/%.c,$(OBJDIR_PIC)/wolfcrypt_%.o,$(WOLFCRYPT_SRC)) +# MBEDTLS_COMPAT=1 adds the Mbed TLS 4.x TLS, X.509 and network API, on +# wolfSSL's TLS compiled from WOLFSSL_PATH like wolfCrypt. +# See src/mbedtls_compat.c and src/mbedtls_net.c. +WOLFSSL_TLS_SRC := +ifeq ($(MBEDTLS_COMPAT),1) +CPPFLAGS += -DWOLFPSA_MBEDTLS_COMPAT +ENROLLMENT_ALG := 1 +WOLFSSL_TLS_SRC := $(addprefix $(WOLFSSL_PATH)/src/,internal.c keys.c ssl.c \ + tls.c tls13.c wolfio.c) +endif +WOLFSSL_TLS_OBJ := $(patsubst $(WOLFSSL_PATH)/src/%.c,$(OBJDIR)/wolfssl_%.o, \ + $(WOLFSSL_TLS_SRC)) +WOLFSSL_TLS_OBJ_PIC := $(patsubst $(WOLFSSL_PATH)/src/%.c, \ + $(OBJDIR_PIC)/wolfssl_%.o,$(WOLFSSL_TLS_SRC)) + +# MBEDTLS_THREADING_C=1 builds a thread safe library, as in Mbed TLS. See +# user_settings.h. Applications define MBEDTLS_THREADING_C too. +ifeq ($(MBEDTLS_THREADING_C),1) +CPPFLAGS += -DMBEDTLS_THREADING_C +LDFLAGS += -pthread +endif + +# Changing MBEDTLS_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 CPPFLAGS += -I$(WOLFSSL_PATH) -I$(USER_SETTINGS_PATH) \ - -I$(CURDIR) -I$(CURDIR)/wolfpsa -I$(CURDIR)/src $(WOLFSSL_CPPFLAGS) + -I$(CURDIR) -I$(CURDIR)/wolfpsa -I$(CURDIR)/src -I$(BUILD_DIR)/include \ + $(WOLFSSL_CPPFLAGS) ifneq ($(strip $(PSA_INCLUDE)),) CPPFLAGS += -I$(PSA_INCLUDE) endif @@ -104,6 +132,10 @@ endif ifeq ($(AES_FAST),1) CPPFLAGS += -DWOLFPSA_AES_FAST endif +# ENROLLMENT_ALG=1 allows the Mbed TLS enrollment algorithm on volatile keys +ifeq ($(ENROLLMENT_ALG),1) +CPPFLAGS += -DWOLFPSA_ENROLLMENT_ALG +endif CFLAGS += $(DEBUG_FLAGS) $(SANITIZE_FLAGS) LDFLAGS += $(SANITIZE_FLAGS) @@ -118,7 +150,8 @@ CFLAGS += --coverage LDFLAGS += --coverage endif -.PHONY: all clean psa-objects unit-run run-tests cov covclean +.PHONY: all clean psa-objects unit-run run-tests cov covclean \ + want-surface-test mbedtls-compat-test all: $(LIBNAME) $(SHLIBNAME) @@ -128,13 +161,44 @@ 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. +# Consumers of add $(BUILD_DIR)/include to the include path. +PSA_CONFIG := $(BUILD_DIR)/include/crypto_config_generated.h +PYTHON ?= python3 + +$(PSA_CONFIG): FORCE + @mkdir -p $(dir $@) + $(PYTHON) tools/gen-crypto-config.py $@ "$(CC)" \ + tools/psa_want_probe.c $(CPPFLAGS) $(CFLAGS) + +$(OBJ) $(OBJ_PIC): $(PSA_CONFIG) + +FORCE: + +# Check the PSA_WANT_* surface of this build against its static library +want-surface-test: $(LIBNAME) + $(CC) $(CPPFLAGS) $(CFLAGS) -o $(BUILD_DIR)/psa_want_surface_test \ + test/psa_server/psa_want_surface_test.c $(LIBNAME) $(LDFLAGS) \ + -lpthread -lm + $(BUILD_DIR)/psa_want_surface_test + +$(OBJ) $(WOLFCRYPT_OBJ) $(WOLFSSL_TLS_OBJ) $(OBJ_PIC) $(WOLFCRYPT_OBJ_PIC) \ + $(WOLFSSL_TLS_OBJ_PIC): $(OPTIONS_STAMP) + +$(OPTIONS_STAMP): + @mkdir -p $(@D) + rm -f $(BUILD_DIR)/options_* + touch $@ + $(OBJDIR)/%.o: src/%.c @mkdir -p $(OBJDIR) $(CC) $(CPPFLAGS) $(DEPFLAGS) $(CFLAGS) -c $< -o $@ @@ -151,6 +215,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 \ @@ -187,16 +259,19 @@ UNIT_TESTS := psa_api_test \ psa_ecc_curve_caps_test \ psa_xof_no_backend_test \ psa_ecc_sig_len_test \ + psa_rsa_status_test \ psa_xof_set_context_test \ psa_cipher_inplace_test \ psa_cipher_overlap_test \ psa_des3_pkcs7_test \ + psa_aead_call_order_test \ psa_eddsa_mont_export_test \ psa_eddsa_mont_gen_test \ psa_pure_eddsa_context_test \ psa_sign_hash_eddsa_test \ psa_key_infer_bits_test \ psa_cipher_oneshot_len_test \ + psa_reset_outputs_test \ psa_pqc_export_seed_test \ psa_key_declared_bits_test \ psa_import_key_probe_test \ @@ -204,11 +279,17 @@ UNIT_TESTS := psa_api_test \ psa_store_read_open_test \ psa_store_dir_validation_test \ psa_devid_cryptocb_test \ + psa_enrollment_alg_test \ psa_kdf_zeroize_output_test \ psa_import_zero_length_test \ + psa_import_key_data_test \ psa_copy_key_cross_lifetime_test \ psa_zero_capacity_buffer_test \ - psa_tf_psa_extra_test + psa_want_surface_test \ + psa_tf_psa_extra_test \ + psa_tf_psa_pk_test \ + psa_tf_psa_threading_test \ + psa_aead_gcm_short_tag_test # Run the unit test loop from the repo root (psa_rsa_pss_interop_test reads # its certificate relative to the root). Assumes the tests are already built. @@ -224,6 +305,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 @@ -273,5 +370,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 fcfeb8f..a035abc 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ wolfPSA is a PSA Crypto **engine** that implements the PSA Crypto API on - wolfCrypt as the backend crypto provider. - The default make target creates a static library (`libwolfpsa.a`) built directly from wolfPSA sources plus the required wolfCrypt objects. +- TF-PSA-Crypto's `mbedtls/pk.h` for private keys: `mbedtls_pk_init`, `free`, + `parse_key`, `parse_keyfile` and `can_do_psa`. The context has the key's + DER and nothing else. RSA and EC keys, PEM or DER, traditional or PKCS #8. + Encrypted keys and EC keys with a compressed public key are not supported. ## Repository layout @@ -60,12 +64,132 @@ AddressSanitizer build: make ASAN=1 ``` +Thread-safe build, as Mbed TLS with `MBEDTLS_THREADING_C`: wolfCrypt without +`SINGLE_THREADED`, a mutex around the key store (`WOLFPSA_THREAD_SAFE`, see +`src/psa_lock.h`) and the TF-PSA-Crypto threading API in +`mbedtls/threading.h`, on pthreads: + +``` +make MBEDTLS_THREADING_C=1 +``` + +Applications define `MBEDTLS_THREADING_C` too, as in their Mbed TLS +configuration, and link with `-pthread`. + You can override the wolfSSL source location: ``` make wolfPSA WOLFSSL_PATH=~/src/wolfssl ``` +The build generates the `PSA_WANT_*` macros from the wolfCrypt configuration +into `$(BUILD_DIR)/include/crypto_config_generated.h` (`build/include` by +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, + dbg +- ssl: init, free, setup, session_reset, set_bio, 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. +- With `VERIFY_REQUIRED`, wolfSSL stops at the first failed check, so + `mbedtls_ssl_get_verify_result` can report fewer flags than Mbed TLS. +- `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`, `threading_test.c` uses the threading API and the +key store from several threads, and `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 0000000..0580477 --- /dev/null +++ b/src/mbedtls_compat.c @@ -0,0 +1,1246 @@ +/* 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 */ +/* For MBEDTLS_X509_BADCERT_MISSING, see MbedVerifyCb() */ +#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]; +} + +/* Map the wolfSSL error behind a failed call to an Mbed TLS return code */ +static int MbedSslErr(const 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; + + /* A failed verify with VERIFY_REQUIRED is what ended the handshake */ + if (ssl->verify_flags != 0 && + ssl->authmode == MBEDTLS_SSL_VERIFY_REQUIRED) { + return MBEDTLS_ERR_X509_CERT_VERIFY_FAILED; + } + 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; +} + +/* Runs for the peer's certificate, also when it sent none, and for each + * failure. VERIFY_OPTIONAL continues the handshake after a failure. */ +static int MbedVerifyCb(int preverify, WOLFSSL_X509_STORE_CTX* store) +{ + mbedtls_ssl_context* s = (mbedtls_ssl_context*)store->userCtx; + + if (store->totalCerts > 0) { + s->peer_cert = 1; + } + if (!preverify) { + s->verify_flags |= MbedVerifyFlag(store->error); + } + return preverify || 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; + } + + wc_InitDecodedCert(cert, buf, (word32)*len, NULL); + ret = wc_ParseCert(cert, CERT_TYPE, NO_VERIFY, NULL); + 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->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) +{ + 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 (; ret == 0 && ca_chain != NULL; ca_chain = ca_chain->next) { + if (ca_chain->raw.p != NULL && + wolfSSL_CTX_load_verify_buffer(conf->ctx, ca_chain->raw.p, + (long)ca_chain->raw.len, WOLFSSL_FILETYPE_ASN1) == + WC_NO_ERR_TRACE(MEMORY_E)) { + ret = MBEDTLS_ERR_SSL_ALLOC_FAILED; + } + } + + 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) { + 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; + } + 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->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; + } +} + +/* 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 0000000..80c8d64 --- /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 0000000..8f185d2 --- /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/src/psa_aead.c b/src/psa_aead.c index 0459860..7719374 100644 --- a/src/psa_aead.c +++ b/src/psa_aead.c @@ -41,6 +41,7 @@ #endif #include "psa_aead_internal.h" #include "psa_size.h" +#include "psa_output.h" static wolfpsa_aead_ctx_t* wolfpsa_aead_get_ctx(psa_aead_operation_t *operation) { @@ -137,6 +138,50 @@ static size_t wolfpsa_aead_tag_length(psa_algorithm_t alg) return PSA_ALG_AEAD_GET_TAG_LENGTH(alg); } +/* CCM puts the lengths in its first block, so it needs them before data */ +static int wolfpsa_aead_lengths_missing(const wolfpsa_aead_ctx_t *ctx) +{ + return PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CCM) && !ctx->lengths_set; +} + +/* SP 800-38D and RFC 8439 limits; the CCM code encodes AAD length in 4 bytes */ +static const struct { + psa_algorithm_t alg; + uint64_t max_ad; + uint64_t max_plaintext; +} wolfpsa_aead_limits[] = { + { PSA_ALG_GCM, (1ULL << 61) - 1, 0xFFFFFFFE0ULL }, + { PSA_ALG_CCM, UINT32_MAX, UINT64_MAX }, + { PSA_ALG_CHACHA20_POLY1305, UINT64_MAX, (uint64_t)UINT32_MAX * 64 }, +}; + +/* A zero nonce_length means the nonce is not set yet */ +static int wolfpsa_aead_lengths_valid(psa_algorithm_t alg, size_t nonce_length, + size_t ad_length, size_t plaintext_length) +{ + size_t i; + size_t len_size; + + /* CCM encodes the plaintext length in the 15 - nonce_length bytes */ + if (PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_CCM) && nonce_length != 0) { + len_size = 15 - nonce_length; + if (len_size < sizeof(uint64_t) && + ((uint64_t)plaintext_length >> (8 * len_size)) != 0) { + return 0; + } + } + + for (i = 0; i < sizeof(wolfpsa_aead_limits) / + sizeof(wolfpsa_aead_limits[0]); i++) { + if (PSA_ALG_AEAD_EQUAL(alg, wolfpsa_aead_limits[i].alg)) { + return (uint64_t)ad_length <= wolfpsa_aead_limits[i].max_ad && + (uint64_t)plaintext_length <= + wolfpsa_aead_limits[i].max_plaintext; + } + } + return 1; +} + #ifdef HAVE_AESGCM static psa_status_t wolfpsa_aead_gcm_check_tag_size(size_t tag_length) { @@ -152,6 +197,33 @@ static psa_status_t wolfpsa_aead_gcm_check_tag_size(size_t tag_length) } #endif +/* Whether permitted algorithm key_alg allows AEAD alg and its tag length */ +static int wolfpsa_aead_alg_permitted(psa_algorithm_t key_alg, + psa_algorithm_t alg) +{ + size_t key_tag_len = wolfpsa_aead_tag_length(key_alg); + size_t req_tag_len = wolfpsa_aead_tag_length(alg); + + if (key_tag_len == 0 || + PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(key_alg) != + PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg)) { + return 0; + } + if ((key_alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + return req_tag_len >= key_tag_len; + } + return req_tag_len == key_tag_len; +} + +/* Whether the permitted or enrollment algorithm allows AEAD alg */ +static int wolfpsa_aead_key_permits_alg(const psa_key_attributes_t *attributes, + psa_algorithm_t alg) +{ + return wolfpsa_aead_alg_permitted(psa_get_key_algorithm(attributes), alg) || + wolfpsa_aead_alg_permitted( + psa_get_key_enrollment_algorithm(attributes), alg); +} + static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, psa_key_usage_t usage, psa_algorithm_t alg, @@ -161,11 +233,6 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; - psa_algorithm_t key_base; - psa_algorithm_t req_base; - size_t key_tag_len; - size_t req_tag_len; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -207,43 +274,14 @@ static psa_status_t wolfpsa_aead_check_key(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - key_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(key_alg); - req_base = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG(alg); - key_tag_len = wolfpsa_aead_tag_length(key_alg); - req_tag_len = wolfpsa_aead_tag_length(alg); - - if (key_tag_len == 0 || req_tag_len == 0) { + if (wolfpsa_aead_tag_length(alg) == 0) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; return PSA_ERROR_INVALID_ARGUMENT; } - if (key_base != req_base) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - if ((key_alg & PSA_ALG_AEAD_AT_LEAST_THIS_LENGTH_FLAG) != 0) { - if (req_tag_len < key_tag_len) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (req_tag_len != key_tag_len) { + if (!wolfpsa_aead_key_permits_alg(attributes, alg)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; @@ -411,15 +449,21 @@ psa_status_t psa_aead_set_lengths(psa_aead_operation_t *operation, size_t plaintext_length) { wolfpsa_aead_ctx_t *ctx = wolfpsa_aead_get_ctx(operation); + psa_status_t status = PSA_SUCCESS; if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } - if (ctx->lengths_set) { - return PSA_ERROR_BAD_STATE; + if (ctx->lengths_set || ctx->ad_started || ctx->body_started) { + status = PSA_ERROR_BAD_STATE; } - if (ctx->nonce_length != 0 || ctx->aad_length != 0 || ctx->input_length != 0) { - return PSA_ERROR_BAD_STATE; + else if (!wolfpsa_aead_lengths_valid(ctx->alg, ctx->nonce_length, + ad_length, plaintext_length)) { + status = PSA_ERROR_INVALID_ARGUMENT; + } + if (status != PSA_SUCCESS) { + psa_aead_abort(operation); + return status; } ctx->ad_expected = ad_length; @@ -445,10 +489,6 @@ psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation, return PSA_ERROR_BAD_STATE; } - if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CCM) && !ctx->lengths_set) { - return PSA_ERROR_BAD_STATE; - } - if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_GCM)) { /* GCM (SP 800-38D) accepts any non-empty nonce. A zero-length nonce is * invalid for the algorithm; other lengths outside the supported 12 to @@ -472,6 +512,11 @@ psa_status_t psa_aead_set_nonce(psa_aead_operation_t *operation, return PSA_ERROR_INVALID_ARGUMENT; } } + if (ctx->lengths_set && + !wolfpsa_aead_lengths_valid(ctx->alg, nonce_length, ctx->ad_expected, + ctx->plaintext_expected)) { + return PSA_ERROR_INVALID_ARGUMENT; + } XMEMCPY(ctx->nonce, nonce, nonce_length); ctx->nonce_length = nonce_length; @@ -487,6 +532,7 @@ psa_status_t psa_aead_generate_nonce(psa_aead_operation_t *operation, size_t expected; psa_status_t status; + wolfpsa_clear_length(nonce_length); if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } @@ -501,9 +547,6 @@ psa_status_t psa_aead_generate_nonce(psa_aead_operation_t *operation, if (ctx->nonce_length != 0) { return PSA_ERROR_BAD_STATE; } - if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CCM) && !ctx->lengths_set) { - return PSA_ERROR_BAD_STATE; - } expected = PSA_AEAD_NONCE_LENGTH(ctx->key_type, ctx->alg); if (expected == 0 || nonce_size < expected) { @@ -534,7 +577,7 @@ psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation, if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } - if (ctx->streaming) { + if (ctx->body_started) { /* The AAD is fed to the streaming primitive in one shot on the first * data update: AAD appended after that would never be authenticated. * The PSA Crypto API forbids the sequence anyway -- all AAD must be @@ -543,11 +586,12 @@ psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation, psa_aead_abort(operation); return status; } - if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CCM) && !ctx->lengths_set) { + if (ctx->nonce_length == 0 || wolfpsa_aead_lengths_missing(ctx)) { return PSA_ERROR_BAD_STATE; } - if (ctx->nonce_length == 0) { - return PSA_ERROR_BAD_STATE; + if (input_length == 0) { + ctx->ad_started = 1; + return PSA_SUCCESS; } if (ctx->lengths_set && (input_length > SIZE_MAX - ctx->aad_length || @@ -567,7 +611,10 @@ psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation, } status = wolfpsa_aead_append(&ctx->aad, &ctx->aad_length, input, input_length); - if (status != PSA_SUCCESS) { + if (status == PSA_SUCCESS) { + ctx->ad_started = 1; + } + else { psa_aead_abort(operation); } return status; @@ -1017,6 +1064,7 @@ psa_status_t psa_aead_update(psa_aead_operation_t *operation, wolfpsa_aead_ctx_t *ctx = wolfpsa_aead_get_ctx(operation); psa_status_t status; + wolfpsa_clear_length(output_length); if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } @@ -1025,9 +1073,8 @@ psa_status_t psa_aead_update(psa_aead_operation_t *operation, } (void)output; (void)output_size; - *output_length = 0; - if (PSA_ALG_AEAD_EQUAL(ctx->alg, PSA_ALG_CCM) && !ctx->lengths_set) { + if (wolfpsa_aead_lengths_missing(ctx)) { return PSA_ERROR_BAD_STATE; } if (ctx->nonce_length == 0) { @@ -1050,6 +1097,8 @@ psa_status_t psa_aead_update(psa_aead_operation_t *operation, psa_aead_abort(operation); return status; } + /* Any failure from here on aborts the operation */ + ctx->body_started = 1; if (input_length == 0) { return PSA_SUCCESS; } @@ -1130,7 +1179,7 @@ static psa_status_t wolfpsa_aead_encrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } - if (ctx->nonce_length == 0) { + if (ctx->nonce_length == 0 || wolfpsa_aead_lengths_missing(ctx)) { return PSA_ERROR_BAD_STATE; } @@ -1307,7 +1356,7 @@ static psa_status_t wolfpsa_aead_decrypt_final(wolfpsa_aead_ctx_t *ctx, return PSA_ERROR_INVALID_ARGUMENT; } - if (ctx->nonce_length == 0) { + if (ctx->nonce_length == 0 || wolfpsa_aead_lengths_missing(ctx)) { return PSA_ERROR_BAD_STATE; } @@ -1470,6 +1519,8 @@ psa_status_t psa_aead_finish(psa_aead_operation_t *operation, wolfpsa_aead_ctx_t *ctx = wolfpsa_aead_get_ctx(operation); psa_status_t status; + wolfpsa_clear_length(ciphertext_length); + wolfpsa_clear_length(tag_length); if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } @@ -1495,6 +1546,7 @@ psa_status_t psa_aead_verify(psa_aead_operation_t *operation, wolfpsa_aead_ctx_t *ctx = wolfpsa_aead_get_ctx(operation); psa_status_t status; + wolfpsa_clear_length(plaintext_length); if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } @@ -1525,7 +1577,6 @@ static psa_status_t wolfpsa_xchacha_oneshot_encrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; int ret; /* Encrypt produces plaintext_length bytes of ciphertext plus 16-byte tag. */ size_t out_len; @@ -1569,12 +1620,7 @@ static psa_status_t wolfpsa_xchacha_oneshot_encrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_XCHACHA20_POLY1305)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -1611,7 +1657,6 @@ static psa_status_t wolfpsa_xchacha_oneshot_decrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; int ret; size_t pt_len; @@ -1652,12 +1697,7 @@ static psa_status_t wolfpsa_xchacha_oneshot_decrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_XCHACHA20_POLY1305)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -1701,7 +1741,6 @@ static psa_status_t wolfpsa_ascon_oneshot_encrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; wc_AsconAEAD128 ascon; int ret; size_t out_len; @@ -1749,12 +1788,7 @@ static psa_status_t wolfpsa_ascon_oneshot_encrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_ASCON_AEAD128)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -1807,7 +1841,6 @@ static psa_status_t wolfpsa_ascon_oneshot_decrypt( size_t key_data_length = 0; psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; wc_AsconAEAD128 ascon; int ret; size_t ct_len; /* ciphertext body length (without tag) */ @@ -1854,12 +1887,7 @@ static psa_status_t wolfpsa_ascon_oneshot_decrypt( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(&attributes); - if (!PSA_ALG_AEAD_EQUAL(key_alg, PSA_ALG_ASCON_AEAD128)) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (!PSA_ALG_AEAD_EQUAL(key_alg, alg)) { + if (!wolfpsa_aead_key_permits_alg(&attributes, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -1918,6 +1946,7 @@ psa_status_t psa_aead_encrypt(psa_key_id_t key, size_t tag_length = 0; size_t out_len = 0; + wolfpsa_clear_length(ciphertext_length); /* One-shot-only algorithms: bypass the multipart state machine. */ #ifdef HAVE_XCHACHA if (PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_XCHACHA20_POLY1305)) { @@ -2008,6 +2037,7 @@ psa_status_t psa_aead_decrypt(psa_key_id_t key, size_t tag_length; size_t ct_len; + wolfpsa_clear_length(plaintext_length); /* One-shot-only algorithms: bypass the multipart state machine. */ #ifdef HAVE_XCHACHA if (PSA_ALG_AEAD_EQUAL(alg, PSA_ALG_XCHACHA20_POLY1305)) { diff --git a/src/psa_aead_internal.h b/src/psa_aead_internal.h index 2490e9d..1e0fc0f 100644 --- a/src/psa_aead_internal.h +++ b/src/psa_aead_internal.h @@ -48,6 +48,8 @@ typedef struct wolfpsa_aead_ctx { size_t plaintext_expected; size_t tag_length; int lengths_set; + int ad_started; + int body_started; /* Streaming state. Used when psa_aead_update() is called with a non-NULL * output: the payload is emitted from update() and only the tag (or, for diff --git a/src/psa_api_stub.c b/src/psa_api_stub.c index 03d0094..315e8c3 100644 --- a/src/psa_api_stub.c +++ b/src/psa_api_stub.c @@ -23,6 +23,7 @@ #include #include +#include "psa_output.h" extern int wolfPSA_CryptoIsInitialized(void); @@ -182,6 +183,8 @@ uint32_t psa_interruptible_get_max_ops(void) { return (uint32_t)WOLFSSL_ATOMIC_LOAD(wolfPSA_interruptible_max_ops); } +/* Interruptible setup never succeeds, so no operation is ever active. */ + /* --- Interruptible sign/verify hash --- */ psa_status_t psa_sign_hash_start( @@ -204,15 +207,12 @@ psa_status_t psa_sign_hash_complete( (void)signature; (void)signature_size; (void)signature_length; - return wolfPSA_StubNotSupported(); + return PSA_ERROR_BAD_STATE; } psa_status_t psa_sign_hash_abort( psa_sign_hash_interruptible_operation_t *operation) { (void)operation; - if (!wolfPSA_CryptoIsInitialized()) { - return PSA_ERROR_BAD_STATE; - } return PSA_SUCCESS; } @@ -240,15 +240,12 @@ psa_status_t psa_verify_hash_start( psa_status_t psa_verify_hash_complete( psa_verify_hash_interruptible_operation_t *operation) { (void)operation; - return wolfPSA_StubNotSupported(); + return PSA_ERROR_BAD_STATE; } psa_status_t psa_verify_hash_abort( psa_verify_hash_interruptible_operation_t *operation) { (void)operation; - if (!wolfPSA_CryptoIsInitialized()) { - return PSA_ERROR_BAD_STATE; - } return PSA_SUCCESS; } @@ -281,15 +278,12 @@ psa_status_t psa_key_agreement_iop_complete( psa_key_id_t *key) { (void)operation; (void)key; - return wolfPSA_StubNotSupported(); + return PSA_ERROR_BAD_STATE; } psa_status_t psa_key_agreement_iop_abort( psa_key_agreement_iop_t *operation) { (void)operation; - if (!wolfPSA_CryptoIsInitialized()) { - return PSA_ERROR_BAD_STATE; - } return PSA_SUCCESS; } @@ -313,15 +307,12 @@ psa_status_t psa_generate_key_iop_complete( psa_key_id_t *key) { (void)operation; (void)key; - return wolfPSA_StubNotSupported(); + return PSA_ERROR_BAD_STATE; } psa_status_t psa_generate_key_iop_abort( psa_generate_key_iop_t *operation) { (void)operation; - if (!wolfPSA_CryptoIsInitialized()) { - return PSA_ERROR_BAD_STATE; - } return PSA_SUCCESS; } @@ -349,15 +340,12 @@ psa_status_t psa_export_public_key_iop_complete( (void)data; (void)data_size; (void)data_length; - return wolfPSA_StubNotSupported(); + return PSA_ERROR_BAD_STATE; } psa_status_t psa_export_public_key_iop_abort( psa_export_public_key_iop_t *operation) { (void)operation; - if (!wolfPSA_CryptoIsInitialized()) { - return PSA_ERROR_BAD_STATE; - } return PSA_SUCCESS; } @@ -374,6 +362,7 @@ psa_status_t psa_generate_key_custom(const psa_key_attributes_t *attributes, const uint8_t *custom_data, size_t custom_data_length, psa_key_id_t *key) { + wolfpsa_clear_key_id(key); if (custom == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -395,6 +384,7 @@ psa_status_t psa_key_derivation_output_key_custom( const uint8_t *custom_data, size_t custom_data_length, psa_key_id_t *key) { + wolfpsa_clear_key_id(key); if (custom == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } diff --git a/src/psa_asymmetric_api.c b/src/psa_asymmetric_api.c index b241b02..c80aee9 100644 --- a/src/psa_asymmetric_api.c +++ b/src/psa_asymmetric_api.c @@ -29,6 +29,7 @@ #include #include "psa_size.h" +#include "psa_output.h" #include "psa_trace.h" #include "psa_pqc_internal.h" #include @@ -105,6 +106,7 @@ psa_status_t psa_asymmetric_decrypt_rsa(psa_key_type_t key_type, uint8_t *output, size_t output_size, size_t *output_length); +#ifdef HAVE_ECC psa_status_t psa_asymmetric_sign_ecc(psa_key_type_t key_type, size_t key_bits, const uint8_t *key_buffer, @@ -124,6 +126,7 @@ psa_status_t psa_asymmetric_verify_ecc(psa_key_type_t key_type, size_t hash_length, const uint8_t *signature, size_t signature_length); +#endif /* HAVE_ECC */ #ifdef HAVE_ED25519 psa_status_t psa_asymmetric_sign_ed25519(psa_key_type_t key_type, size_t key_bits, @@ -221,26 +224,10 @@ static int wolfpsa_sign_alg_permitted(psa_algorithm_t key_alg, } /* Standard PSA_ALG_ANY_HASH wildcard for hash-and-sign families */ if (PSA_ALG_IS_SIGN_HASH(alg) && - PSA_ALG_SIGN_GET_HASH(key_alg) == PSA_ALG_ANY_HASH) { - return (PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_ANY_HASH) && - ((key_alg & ~PSA_ALG_HASH_MASK) == (alg & ~PSA_ALG_HASH_MASK)); - } - /* PSA_ALG_ANY_HASH wildcard for HashML-DSA and DeterministicHashML-DSA. - * PSA_ALG_IS_HASH_ML_DSA matches both families (its mask covers the - * 0x100 family selector bit), so gate on the hedged predicate and - * compare with the hash-only mask: a wildcard policy must not cross - * the hedged/deterministic boundary. */ - if (PSA_ALG_IS_HEDGED_HASH_ML_DSA(alg) && - PSA_ALG_IS_HEDGED_HASH_ML_DSA(key_alg) && - PSA_ALG_GET_HASH(key_alg) == PSA_ALG_ANY_HASH) { - return (PSA_ALG_GET_HASH(alg) != PSA_ALG_ANY_HASH) && - ((key_alg & ~0x000000ffU) == (alg & ~0x000000ffU)); - } - if (PSA_ALG_IS_DETERMINISTIC_HASH_ML_DSA(alg) && - PSA_ALG_IS_DETERMINISTIC_HASH_ML_DSA(key_alg) && - PSA_ALG_GET_HASH(key_alg) == PSA_ALG_ANY_HASH) { - return (PSA_ALG_GET_HASH(alg) != PSA_ALG_ANY_HASH) && - ((key_alg & ~0x000000ffU) == (alg & ~0x000000ffU)); + PSA_ALG_SIGN_GET_HASH(key_alg) == PSA_ALG_ANY_HASH && + PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_ANY_HASH && + (key_alg & ~PSA_ALG_HASH_MASK) == (alg & ~PSA_ALG_HASH_MASK)) { + return 1; } /* PSA 1.4 ECDSA verify-equivalence: for verify usages, ECDSA and * DETERMINISTIC_ECDSA with the same hash are interchangeable. @@ -263,9 +250,7 @@ static int wolfpsa_sign_alg_permitted(psa_algorithm_t key_alg, if (PSA_ALG_IS_HASH_ML_DSA(alg) && PSA_ALG_IS_HASH_ML_DSA(key_alg) && PSA_ALG_GET_HASH(alg) != PSA_ALG_ANY_HASH) { - /* A wildcard is a policy placeholder, not an executable - * algorithm: the request must name a concrete hash, the - * same invariant the wildcard blocks above enforce. */ + /* A wildcard is a policy, so the request must name a hash */ if (PSA_ALG_GET_HASH(key_alg) == PSA_ALG_ANY_HASH || PSA_ALG_GET_HASH(key_alg) == PSA_ALG_GET_HASH(alg)) { return 1; @@ -275,6 +260,19 @@ static int wolfpsa_sign_alg_permitted(psa_algorithm_t key_alg, return 0; } +static int wolfpsa_asymmetric_alg_permitted(psa_algorithm_t key_alg, + psa_algorithm_t alg, + psa_key_usage_t usage) +{ + if (key_alg == PSA_ALG_NONE) { + return 0; + } + if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { + return wolfpsa_key_agreement_alg_permitted(key_alg, alg); + } + return wolfpsa_sign_alg_permitted(key_alg, alg, usage); +} + static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, psa_key_usage_t usage, psa_algorithm_t alg, @@ -284,7 +282,6 @@ static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -299,24 +296,10 @@ static psa_status_t wolfpsa_asymmetric_check_key(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - if (PSA_ALG_IS_KEY_AGREEMENT(alg) && PSA_ALG_IS_KEY_AGREEMENT(key_alg)) { - if (!wolfpsa_key_agreement_alg_permitted(key_alg, alg)) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (!wolfpsa_sign_alg_permitted(key_alg, alg, usage)) { + if (!wolfpsa_asymmetric_alg_permitted(psa_get_key_algorithm(attributes), + alg, usage) && + !wolfpsa_asymmetric_alg_permitted( + psa_get_key_enrollment_algorithm(attributes), alg, usage)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; @@ -367,6 +350,20 @@ static psa_status_t wolfpsa_check_context(psa_algorithm_t alg, return PSA_ERROR_INVALID_ARGUMENT; } +/* Required signature size, or 0 when this build cannot sign with key_type */ +static size_t wolfpsa_sign_output_size(psa_key_type_t key_type, + size_t key_bits, + psa_algorithm_t alg) +{ +#ifndef HAVE_ECC + if (PSA_KEY_TYPE_IS_ECC(key_type) && + key_type != PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)) { + return 0; + } +#endif /* !HAVE_ECC */ + return PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg); +} + psa_status_t psa_asymmetric_encrypt(psa_key_id_t key, psa_algorithm_t alg, const uint8_t *input, @@ -382,6 +379,7 @@ psa_status_t psa_asymmetric_encrypt(psa_key_id_t key, size_t key_data_length = 0; psa_status_t status; + wolfpsa_clear_length(output_length); if (output_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -430,6 +428,7 @@ psa_status_t psa_asymmetric_decrypt(psa_key_id_t key, size_t key_data_length = 0; psa_status_t status; + wolfpsa_clear_length(output_length); if (output_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -480,6 +479,7 @@ static psa_status_t wolfpsa_sign_hash_worker(psa_key_id_t key, size_t sig_size; psa_status_t status; + wolfpsa_clear_length(signature_length); if (signature_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -562,7 +562,7 @@ static psa_status_t wolfpsa_sign_hash_worker(psa_key_id_t key, * contract status for every signature family. Checked after the * algorithm and key-type rejections above: a call that can never succeed * must report why, not a buffer problem. */ - sig_size = PSA_SIGN_OUTPUT_SIZE(attributes.type, attributes.bits, alg); + sig_size = wolfpsa_sign_output_size(attributes.type, attributes.bits, alg); if (sig_size != 0 && signature_size < sig_size) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_BUFFER_TOO_SMALL; @@ -604,6 +604,7 @@ static psa_status_t wolfpsa_sign_hash_worker(psa_key_id_t key, } } #endif +#ifdef HAVE_ECC else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { status = psa_asymmetric_sign_ecc(attributes.type, attributes.bits, key_data, key_data_length, @@ -611,6 +612,7 @@ static psa_status_t wolfpsa_sign_hash_worker(psa_key_id_t key, signature, signature_size, signature_length); } +#endif /* HAVE_ECC */ else { status = PSA_ERROR_NOT_SUPPORTED; } @@ -745,12 +747,14 @@ static psa_status_t wolfpsa_verify_hash_worker(psa_key_id_t key, } } #endif +#ifdef HAVE_ECC else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { status = psa_asymmetric_verify_ecc(attributes.type, attributes.bits, key_data, key_data_length, alg, hash, hash_length, signature, signature_length); } +#endif /* HAVE_ECC */ else { status = PSA_ERROR_NOT_SUPPORTED; } @@ -844,6 +848,7 @@ static psa_status_t wolfpsa_sign_message_worker(psa_key_id_t key, size_t sig_size; psa_status_t status; + wolfpsa_clear_length(signature_length); if (signature_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -871,7 +876,7 @@ static psa_status_t wolfpsa_sign_message_worker(psa_key_id_t key, /* Required signature capacity, so a zero-capacity buffer gets the * contract status for every signature family. */ - sig_size = PSA_SIGN_OUTPUT_SIZE(attributes.type, attributes.bits, alg); + sig_size = wolfpsa_sign_output_size(attributes.type, attributes.bits, alg); if (sig_size != 0 && signature_size < sig_size) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_BUFFER_TOO_SMALL; @@ -1036,6 +1041,7 @@ static psa_status_t wolfpsa_sign_message_worker(psa_key_id_t key, } } #endif +#ifdef HAVE_ECC else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { status = psa_asymmetric_sign_ecc(attributes.type, attributes.bits, key_data, key_data_length, @@ -1043,6 +1049,7 @@ static psa_status_t wolfpsa_sign_message_worker(psa_key_id_t key, signature, signature_size, signature_length); } +#endif /* HAVE_ECC */ else { status = PSA_ERROR_NOT_SUPPORTED; } @@ -1284,12 +1291,14 @@ static psa_status_t wolfpsa_verify_message_worker(psa_key_id_t key, } } #endif +#ifdef HAVE_ECC else if (PSA_KEY_TYPE_IS_ECC(attributes.type)) { status = psa_asymmetric_verify_ecc(attributes.type, attributes.bits, key_data, key_data_length, alg, hash, hash_length, signature, signature_length); } +#endif /* HAVE_ECC */ else { status = PSA_ERROR_NOT_SUPPORTED; } @@ -1576,6 +1585,7 @@ psa_status_t psa_raw_key_agreement(psa_algorithm_t alg, wolfpsa_trace("psa_raw_key_agreement(alg=0x%08x key=%u peer_len=%zu)", (unsigned)alg, (unsigned)private_key, peer_key_length); + wolfpsa_clear_length(output_length); if (output_length == NULL || (output == NULL && output_size != 0)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -1588,6 +1598,43 @@ psa_status_t psa_raw_key_agreement(psa_algorithm_t alg, output_length); } +static psa_status_t wolfpsa_key_agreement_kdf(psa_key_id_t private_key, + const uint8_t *peer_key, + size_t peer_key_length, + psa_algorithm_t alg, + const psa_key_attributes_t * + attributes, + psa_key_id_t *key) +{ + psa_algorithm_t kdf_alg = PSA_ALG_KEY_AGREEMENT_GET_KDF(alg); + psa_key_derivation_operation_t kdf_op = PSA_KEY_DERIVATION_OPERATION_INIT; + psa_status_t status; + + status = psa_key_derivation_setup(&kdf_op, alg); + if (status == PSA_SUCCESS && + (PSA_ALG_IS_HKDF(kdf_alg) || PSA_ALG_IS_HKDF_EXTRACT(kdf_alg))) { + status = psa_key_derivation_input_bytes(&kdf_op, + PSA_KEY_DERIVATION_INPUT_SALT, + NULL, 0); + } + if (status == PSA_SUCCESS) { + status = psa_key_derivation_key_agreement( + &kdf_op, PSA_KEY_DERIVATION_INPUT_SECRET, private_key, peer_key, + peer_key_length); + } + if (status == PSA_SUCCESS && + (PSA_ALG_IS_HKDF(kdf_alg) || PSA_ALG_IS_HKDF_EXPAND(kdf_alg))) { + status = psa_key_derivation_input_bytes(&kdf_op, + PSA_KEY_DERIVATION_INPUT_INFO, + NULL, 0); + } + if (status == PSA_SUCCESS) { + status = psa_key_derivation_output_key(attributes, &kdf_op, key); + } + psa_key_derivation_abort(&kdf_op); + return status; +} + psa_status_t psa_key_agreement(psa_key_id_t private_key, const uint8_t *peer_key, size_t peer_key_length, @@ -1599,19 +1646,24 @@ psa_status_t psa_key_agreement(psa_key_id_t private_key, size_t secret_len; size_t output_len = 0; psa_status_t status; - psa_algorithm_t kdf_alg; + psa_key_type_t out_type; psa_key_attributes_t priv_attr = PSA_KEY_ATTRIBUTES_INIT; - psa_key_derivation_operation_t kdf_op = PSA_KEY_DERIVATION_OPERATION_INIT; + wolfpsa_clear_key_id(key); if (attributes == NULL || key == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } - *key = PSA_KEY_ID_NULL; if (!PSA_ALG_IS_KEY_AGREEMENT(alg)) { return PSA_ERROR_NOT_SUPPORTED; } + if (!PSA_ALG_IS_RAW_KEY_AGREEMENT(alg)) { + return wolfpsa_key_agreement_kdf(private_key, peer_key, + peer_key_length, alg, attributes, + key); + } + status = psa_get_key_attributes(private_key, &priv_attr); if (status != PSA_SUCCESS) { return status; @@ -1637,62 +1689,16 @@ psa_status_t psa_key_agreement(psa_key_id_t private_key, return status; } - kdf_alg = PSA_ALG_KEY_AGREEMENT_GET_KDF(alg); - if (kdf_alg == PSA_ALG_CATEGORY_KEY_DERIVATION) { - psa_key_type_t out_type = psa_get_key_type(attributes); - - if (out_type != PSA_KEY_TYPE_DERIVE && - out_type != PSA_KEY_TYPE_RAW_DATA) { - wc_ForceZero(secret, secret_len); - XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return PSA_ERROR_INVALID_ARGUMENT; - } - status = psa_import_key(attributes, secret, output_len, key); - wc_ForceZero(secret, secret_len); - XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return status; - } - - status = psa_key_derivation_setup(&kdf_op, kdf_alg); - if (status != PSA_SUCCESS) { - wc_ForceZero(secret, secret_len); - XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return status; + out_type = psa_get_key_type(attributes); + if (out_type != PSA_KEY_TYPE_DERIVE && + out_type != PSA_KEY_TYPE_RAW_DATA) { + status = PSA_ERROR_INVALID_ARGUMENT; } - - if (PSA_ALG_IS_HKDF(kdf_alg) || PSA_ALG_IS_HKDF_EXTRACT(kdf_alg)) { - status = psa_key_derivation_input_bytes(&kdf_op, - PSA_KEY_DERIVATION_INPUT_SALT, - NULL, 0); - if (status != PSA_SUCCESS) { - wc_ForceZero(secret, secret_len); - XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); - psa_key_derivation_abort(&kdf_op); - return status; - } + else { + status = psa_import_key(attributes, secret, output_len, key); } - - status = psa_key_derivation_input_bytes(&kdf_op, PSA_KEY_DERIVATION_INPUT_SECRET, - secret, output_len); wc_ForceZero(secret, secret_len); XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (status != PSA_SUCCESS) { - psa_key_derivation_abort(&kdf_op); - return status; - } - - if (PSA_ALG_IS_HKDF(kdf_alg) || PSA_ALG_IS_HKDF_EXPAND(kdf_alg)) { - status = psa_key_derivation_input_bytes(&kdf_op, - PSA_KEY_DERIVATION_INPUT_INFO, - NULL, 0); - if (status != PSA_SUCCESS) { - psa_key_derivation_abort(&kdf_op); - return status; - } - } - - status = psa_key_derivation_output_key(attributes, &kdf_op, key); - psa_key_derivation_abort(&kdf_op); return status; } diff --git a/src/psa_cipher.c b/src/psa_cipher.c index f64112c..bc3cb88 100644 --- a/src/psa_cipher.c +++ b/src/psa_cipher.c @@ -30,6 +30,7 @@ #include #include "psa_trace.h" #include "psa_size.h" +#include "psa_output.h" #include #include #include @@ -161,7 +162,6 @@ static psa_status_t wolfpsa_cipher_check_key( { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -228,8 +228,7 @@ static psa_status_t wolfpsa_cipher_check_key( return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE || key_alg != alg) { + if (!wolfpsa_key_permits_alg(attributes, alg)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; @@ -584,9 +583,9 @@ psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation, return PSA_SUCCESS; } -psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation, - const uint8_t *iv, - size_t iv_length) +static psa_status_t wolfpsa_cipher_set_iv(psa_cipher_operation_t *operation, + const uint8_t *iv, + size_t iv_length) { wolfpsa_cipher_ctx_t *ctx = wolfpsa_cipher_get_ctx(operation); int ret; @@ -602,14 +601,8 @@ psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation, return PSA_ERROR_INVALID_ARGUMENT; } - if (ctx->alg != PSA_ALG_CBC_NO_PADDING && - ctx->alg != PSA_ALG_CBC_PKCS7 && - ctx->alg != PSA_ALG_CTR && - ctx->alg != PSA_ALG_CFB && - ctx->alg != PSA_ALG_OFB && - ctx->alg != PSA_ALG_STREAM_CIPHER && - ctx->alg != PSA_ALG_CCM_STAR_NO_TAG) { - return PSA_ERROR_NOT_SUPPORTED; + if (wolfpsa_cipher_iv_length(ctx->alg, ctx->key_type) == 0) { + return PSA_ERROR_BAD_STATE; } if (ctx->alg == PSA_ALG_STREAM_CIPHER) { @@ -671,15 +664,27 @@ psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation, return PSA_SUCCESS; } -psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, - uint8_t *iv, - size_t iv_size, - size_t *iv_length) +psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation, + const uint8_t *iv, + size_t iv_length) +{ + psa_status_t status = wolfpsa_cipher_set_iv(operation, iv, iv_length); + + if (status != PSA_SUCCESS) { + return wolfpsa_cipher_fail(operation, status); + } + return status; +} + +static psa_status_t wolfpsa_cipher_generate_iv( + psa_cipher_operation_t *operation, uint8_t *iv, size_t iv_size, + size_t *iv_length) { psa_status_t status; wolfpsa_cipher_ctx_t *ctx = wolfpsa_cipher_get_ctx(operation); size_t expected_len; + wolfpsa_clear_length(iv_length); if (ctx == NULL) { return PSA_ERROR_BAD_STATE; } @@ -697,7 +702,7 @@ psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, expected_len = wolfpsa_cipher_iv_length(ctx->alg, ctx->key_type); if (expected_len == 0) { - return PSA_ERROR_NOT_SUPPORTED; + return PSA_ERROR_BAD_STATE; } if (iv_size < expected_len) { return PSA_ERROR_BUFFER_TOO_SMALL; @@ -708,7 +713,7 @@ psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, return status; } - status = psa_cipher_set_iv(operation, iv, expected_len); + status = wolfpsa_cipher_set_iv(operation, iv, expected_len); if (status != PSA_SUCCESS) { return status; } @@ -717,6 +722,20 @@ psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, return PSA_SUCCESS; } +psa_status_t psa_cipher_generate_iv(psa_cipher_operation_t *operation, + uint8_t *iv, + size_t iv_size, + size_t *iv_length) +{ + psa_status_t status = wolfpsa_cipher_generate_iv(operation, iv, iv_size, + iv_length); + + if (status != PSA_SUCCESS) { + return wolfpsa_cipher_fail(operation, status); + } + return status; +} + psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, const uint8_t *input, size_t input_length, @@ -1363,6 +1382,7 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, wolfpsa_cipher_ctx_t *ctx = wolfpsa_cipher_get_ctx(operation); int ret = 0; + wolfpsa_clear_length(output_length); if (operation == NULL || output_length == NULL) { return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } @@ -1495,7 +1515,6 @@ psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, return wolfpsa_cipher_fail(operation, PSA_ERROR_INVALID_ARGUMENT); } - *output_length = 0; psa_cipher_abort(operation); return PSA_SUCCESS; } @@ -1542,6 +1561,7 @@ psa_status_t psa_cipher_encrypt(psa_key_id_t key, size_t offset = 0; wolfpsa_cipher_ctx_t *ctx; + wolfpsa_clear_length(output_length); if (output_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -1636,6 +1656,7 @@ psa_status_t psa_cipher_decrypt(psa_key_id_t key, size_t offset = 0; wolfpsa_cipher_ctx_t *ctx; + wolfpsa_clear_length(output_length); if (output_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } diff --git a/src/psa_hash_engine.c b/src/psa_hash_engine.c index dd162ad..9c6b4df 100644 --- a/src/psa_hash_engine.c +++ b/src/psa_hash_engine.c @@ -30,6 +30,7 @@ #include #include "psa_trace.h" #include "psa_size.h" +#include "psa_output.h" #include #include #include @@ -605,6 +606,7 @@ psa_status_t psa_hash_finish(psa_hash_operation_t *operation, size_t expected_hash_size; psa_hash_operation_ctx_t *ctx = psa_hash_get_ctx(operation); + wolfpsa_clear_length(hash_length); /* A NULL hash pointer is only an error when the caller declared a * nonzero capacity; (NULL, 0) must reach the size check below. */ if (operation == NULL || hash_length == NULL || @@ -934,6 +936,7 @@ psa_status_t psa_hash_compute(psa_algorithm_t alg, psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT; psa_status_t status; + wolfpsa_clear_length(hash_length); if (!wolfPSA_CryptoIsInitialized()) { return PSA_ERROR_BAD_STATE; } diff --git a/src/psa_kem.c b/src/psa_kem.c index 5f99f62..a7992fb 100644 --- a/src/psa_kem.c +++ b/src/psa_kem.c @@ -32,6 +32,7 @@ #include #include "psa_pqc_internal.h" #include "psa_trace.h" +#include "psa_output.h" #include #include #include @@ -88,7 +89,6 @@ psa_status_t psa_encapsulate(psa_key_id_t key, psa_key_type_t key_type; size_t key_bits; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; psa_status_t status; uint8_t ss[WOLFPSA_MLKEM_SS_SIZE]; /* Staging buffer: ciphertext is written here before the output key is @@ -99,6 +99,8 @@ psa_status_t psa_encapsulate(psa_key_id_t key, wolfpsa_trace("psa_encapsulate(key=%u alg=0x%08x)", (unsigned)key, (unsigned)alg); + wolfpsa_clear_key_id(output_key); + wolfpsa_clear_length(ciphertext_length); /* --- Argument validation --- * A NULL ciphertext pointer is only an error when the caller declared a * nonzero capacity; (NULL, 0) must reach the size check below. */ @@ -107,9 +109,6 @@ psa_status_t psa_encapsulate(psa_key_id_t key, return PSA_ERROR_INVALID_ARGUMENT; } - *output_key = PSA_KEY_ID_NULL; - *ciphertext_length = 0; - status = wolfpsa_kem_check_alg(alg); if (status != PSA_SUCCESS) { return status; @@ -129,7 +128,6 @@ psa_status_t psa_encapsulate(psa_key_id_t key, key_type = psa_get_key_type(&key_attr); key_bits = psa_get_key_bits(&key_attr); key_usage = psa_get_key_usage_flags(&key_attr); - key_alg = psa_get_key_algorithm(&key_attr); if (key_type != PSA_KEY_TYPE_ML_KEM_KEY_PAIR && key_type != PSA_KEY_TYPE_ML_KEM_PUBLIC_KEY) { @@ -142,11 +140,7 @@ psa_status_t psa_encapsulate(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - if (key_alg != alg && key_alg != PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (key_alg == PSA_ALG_NONE) { + if (!wolfpsa_key_permits_alg(&key_attr, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -207,20 +201,18 @@ psa_status_t psa_decapsulate(psa_key_id_t key, psa_key_type_t key_type; size_t key_bits; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; psa_status_t status; uint8_t ss[WOLFPSA_MLKEM_SS_SIZE]; wolfpsa_trace("psa_decapsulate(key=%u alg=0x%08x ct_len=%zu)", (unsigned)key, (unsigned)alg, ciphertext_length); + wolfpsa_clear_key_id(output_key); /* --- Argument validation --- */ if (output_key == NULL || ciphertext == NULL || attributes == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } - *output_key = PSA_KEY_ID_NULL; - status = wolfpsa_kem_check_alg(alg); if (status != PSA_SUCCESS) { return status; @@ -240,7 +232,6 @@ psa_status_t psa_decapsulate(psa_key_id_t key, key_type = psa_get_key_type(&key_attr); key_bits = psa_get_key_bits(&key_attr); key_usage = psa_get_key_usage_flags(&key_attr); - key_alg = psa_get_key_algorithm(&key_attr); /* Decapsulate requires the key pair (private key). */ if (key_type != PSA_KEY_TYPE_ML_KEM_KEY_PAIR) { @@ -253,11 +244,7 @@ psa_status_t psa_decapsulate(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - if (key_alg != alg && key_alg != PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - if (key_alg == PSA_ALG_NONE) { + if (!wolfpsa_key_permits_alg(&key_attr, alg)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } diff --git a/src/psa_key_derivation.c b/src/psa_key_derivation.c index 60f3faf..c022c40 100644 --- a/src/psa_key_derivation.c +++ b/src/psa_key_derivation.c @@ -31,6 +31,7 @@ #include #include "psa_size.h" +#include "psa_output.h" #include #include #include @@ -84,6 +85,7 @@ typedef struct wolfpsa_kdf_ctx { int is_key_agreement; int is_raw_kdf; int output_started; + int can_output_key; /* PSA: once a call on the operation fails, the operation is in an error * state and only psa_key_derivation_abort() may follow. */ int error_state; @@ -108,6 +110,22 @@ typedef struct wolfpsa_kdf_ctx { #define WOLFPSA_KDF_STEP_COST (1u << 7) #define WOLFPSA_KDF_STEP_CONTEXT (1u << 8) +/* Key types psa_key_derivation_input_key() accepts for each step */ +static const struct { + psa_key_derivation_step_t step; + psa_key_type_t type; +} wolfpsa_kdf_key_inputs[] = { + { PSA_KEY_DERIVATION_INPUT_SECRET, PSA_KEY_TYPE_DERIVE }, + { PSA_KEY_DERIVATION_INPUT_OTHER_SECRET, PSA_KEY_TYPE_DERIVE }, + { PSA_KEY_DERIVATION_INPUT_PASSWORD, PSA_KEY_TYPE_PASSWORD }, + { PSA_KEY_DERIVATION_INPUT_PASSWORD, PSA_KEY_TYPE_DERIVE }, + { PSA_KEY_DERIVATION_INPUT_SALT, PSA_KEY_TYPE_RAW_DATA }, + { PSA_KEY_DERIVATION_INPUT_INFO, PSA_KEY_TYPE_RAW_DATA }, + { PSA_KEY_DERIVATION_INPUT_LABEL, PSA_KEY_TYPE_RAW_DATA }, + { PSA_KEY_DERIVATION_INPUT_CONTEXT, PSA_KEY_TYPE_RAW_DATA }, + { PSA_KEY_DERIVATION_INPUT_SEED, PSA_KEY_TYPE_RAW_DATA }, +}; + /* Upper bound on how much of a bounded-capacity derivation is pre-computed * and cached on the first output_bytes() call. HKDF's default capacity is at * most 255 * hash_len (roughly 16 KB), which fits comfortably. SP800-108 @@ -843,6 +861,45 @@ psa_status_t psa_key_derivation_input_integer(psa_key_derivation_operation_t * wolfpsa_kdf_input_integer(operation, step, value)); } +static int wolfpsa_kdf_key_type_ok(psa_algorithm_t alg, + psa_key_derivation_step_t step, + psa_key_type_t type) +{ + size_t i; + + /* SP 800-108 also takes the MAC key itself as the secret */ + if (step == PSA_KEY_DERIVATION_INPUT_SECRET && + ((type == PSA_KEY_TYPE_HMAC && + PSA_ALG_IS_SP800_108_COUNTER_HMAC(alg)) || + (type == PSA_KEY_TYPE_AES && + alg == PSA_ALG_SP800_108_COUNTER_CMAC))) { + return 1; + } + + for (i = 0; i < sizeof(wolfpsa_kdf_key_inputs) / + sizeof(wolfpsa_kdf_key_inputs[0]); i++) { + if (wolfpsa_kdf_key_inputs[i].step == step && + wolfpsa_kdf_key_inputs[i].type == type) { + return 1; + } + } + return 0; +} + +/* Whether a key with permitted algorithm key_alg can be input to ctx */ +static int wolfpsa_kdf_key_alg_permitted(const wolfpsa_kdf_ctx_t *ctx, + psa_algorithm_t key_alg) +{ + if (key_alg == PSA_ALG_NONE) { + return 0; + } + if (ctx->is_key_agreement) { + return PSA_ALG_IS_KEY_AGREEMENT(key_alg) && + PSA_ALG_KEY_AGREEMENT_GET_KDF(key_alg) == ctx->alg; + } + return key_alg == ctx->alg; +} + static psa_status_t wolfpsa_kdf_input_key(psa_key_derivation_operation_t * operation, psa_key_derivation_step_t step, @@ -852,7 +909,6 @@ static psa_status_t wolfpsa_kdf_input_key(psa_key_derivation_operation_t * psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; uint8_t *key_data = NULL; size_t key_data_length = 0; - psa_algorithm_t key_alg; psa_status_t status; if (ctx == NULL) { @@ -876,59 +932,16 @@ static psa_status_t wolfpsa_kdf_input_key(psa_key_derivation_operation_t * return PSA_ERROR_NOT_PERMITTED; } - if (PSA_ALG_IS_PBKDF2(ctx->alg)) { - if (step != PSA_KEY_DERIVATION_INPUT_PASSWORD || - psa_get_key_type(&attributes) != PSA_KEY_TYPE_PASSWORD) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_INVALID_ARGUMENT; - } - } - else if (step == PSA_KEY_DERIVATION_INPUT_SECRET || - step == PSA_KEY_DERIVATION_INPUT_OTHER_SECRET) { - psa_key_type_t key_type = psa_get_key_type(&attributes); - int compatible = 0; - - /* A generic DERIVE key is valid for any KDF. The SP800-108 - * backends also take a key of the underlying MAC type, whose - * material is the raw MAC key: HMAC keys for the HMAC variant - * and AES keys for the CMAC variant. */ - if (key_type == PSA_KEY_TYPE_DERIVE) { - compatible = 1; - } - else if (key_type == PSA_KEY_TYPE_HMAC && - PSA_ALG_IS_SP800_108_COUNTER_HMAC(ctx->alg)) { - compatible = 1; - } - else if (key_type == PSA_KEY_TYPE_AES && - ctx->alg == PSA_ALG_SP800_108_COUNTER_CMAC) { - compatible = 1; - } - - if (!compatible) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_INVALID_ARGUMENT; - } - } - else { + if (!wolfpsa_kdf_key_type_ok(ctx->alg, step, + psa_get_key_type(&attributes))) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_INVALID_ARGUMENT; } - key_alg = psa_get_key_algorithm(&attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - if (ctx->is_key_agreement) { - if (!PSA_ALG_IS_KEY_AGREEMENT(key_alg) || - PSA_ALG_KEY_AGREEMENT_GET_KDF(key_alg) != ctx->alg) { - wolfpsa_forcezero_free_key_data(key_data, key_data_length); - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (key_alg != ctx->alg) { + if (!wolfpsa_kdf_key_alg_permitted(ctx, + psa_get_key_algorithm(&attributes)) && + !wolfpsa_kdf_key_alg_permitted( + ctx, psa_get_key_enrollment_algorithm(&attributes))) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_NOT_PERMITTED; } @@ -936,6 +949,14 @@ static psa_status_t wolfpsa_kdf_input_key(psa_key_derivation_operation_t * status = wolfpsa_kdf_input_bytes(operation, step, key_data, key_data_length); wolfpsa_forcezero_free_key_data(key_data, key_data_length); + + /* Only a secret or password from a DERIVE key permits output_key */ + if (status == PSA_SUCCESS && + (psa_get_key_usage_flags(&attributes) & PSA_KEY_USAGE_DERIVE) && + (step == PSA_KEY_DERIVATION_INPUT_SECRET || + step == PSA_KEY_DERIVATION_INPUT_PASSWORD)) { + ctx->can_output_key = 1; + } return status; } @@ -1015,6 +1036,9 @@ static psa_status_t wolfpsa_kdf_key_agreement(psa_key_derivation_operation_t * if (status == PSA_SUCCESS) { status = wolfpsa_kdf_input_bytes(operation, step, secret, output_len); } + if (status == PSA_SUCCESS && step == PSA_KEY_DERIVATION_INPUT_SECRET) { + ctx->can_output_key = 1; + } wc_ForceZero(secret, secret_len); XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); @@ -2017,6 +2041,7 @@ static psa_status_t wolfpsa_kdf_output_key(const psa_key_attributes_t * operation, psa_key_id_t *key) { + wolfpsa_kdf_ctx_t *ctx = wolfpsa_kdf_get_ctx(operation); size_t key_len; uint8_t *buffer; psa_status_t status; @@ -2025,12 +2050,16 @@ static psa_status_t wolfpsa_kdf_output_key(const psa_key_attributes_t * return PSA_ERROR_INVALID_ARGUMENT; } - if (!PSA_KEY_TYPE_IS_UNSTRUCTURED(attributes->type)) { + key_len = PSA_BITS_TO_BYTES(attributes->bits); + if (key_len == 0) { return PSA_ERROR_INVALID_ARGUMENT; } - key_len = PSA_BITS_TO_BYTES(attributes->bits); - if (key_len == 0) { + if (!ctx->can_output_key) { + return PSA_ERROR_NOT_PERMITTED; + } + + if (!PSA_KEY_TYPE_IS_UNSTRUCTURED(attributes->type)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -2061,6 +2090,7 @@ psa_status_t psa_key_derivation_output_key(const psa_key_attributes_t * wolfpsa_kdf_ctx_t *ctx = wolfpsa_kdf_get_ctx(operation); psa_status_t status = wolfpsa_kdf_check_state(ctx); + wolfpsa_clear_key_id(key); if (status != PSA_SUCCESS) { return status; } diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 141b3c2..1cc6479 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -34,6 +34,7 @@ #include "psa_trace.h" #include "psa_lock.h" #include "psa_size.h" +#include "psa_output.h" #include #include #include @@ -191,6 +192,7 @@ psa_status_t psa_asymmetric_export_public_key_ecc(psa_key_type_t key_type, size_t *output_length); #endif +#ifndef NO_RSA static psa_status_t psa_wc_error_to_psa_status(int ret) { psa_status_t status; @@ -223,6 +225,7 @@ static psa_status_t psa_wc_error_to_psa_status(int ret) return status; } +#endif /* !NO_RSA */ static psa_status_t wolfpsa_validate_stored_key_data_length(size_t key_data_length) @@ -539,6 +542,74 @@ static psa_status_t wolfpsa_volatile_get_attributes(psa_key_id_t key_id, return st; } +#ifndef NO_RSA +/* Decode RSA key data of the given type and get the modulus size in bits. */ +static psa_status_t wolfpsa_rsa_key_bits(psa_key_type_t type, + const uint8_t* data, + size_t data_length, + psa_key_bits_t* bits) +{ + RsaKey rsa; + word32 idx = 0; + int ret; + int n_bits = 0; + + ret = wc_InitRsaKey(&rsa, NULL); + if (ret == 0) { + if (type == PSA_KEY_TYPE_RSA_PUBLIC_KEY) { + ret = wc_RsaPublicKeyDecode(data, &idx, &rsa, (word32)data_length); + } + else { + ret = wc_RsaPrivateKeyDecode(data, &idx, &rsa, + (word32)data_length); + } + if (ret == 0) { + n_bits = mp_count_bits(&rsa.n); + } + wc_FreeRsaKey(&rsa); + } + + if (ret == MEMORY_E) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + if (ret != 0 || idx != (word32)data_length || n_bits <= 0) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if ((n_bits % 8) != 0 || n_bits > (int)PSA_VENDOR_RSA_MAX_KEY_BITS) { + return PSA_ERROR_NOT_SUPPORTED; + } + *bits = (psa_key_bits_t)n_bits; + return PSA_SUCCESS; +} +#endif /* !NO_RSA */ + +#ifdef HAVE_ECC +/* wolfCrypt checks that the private scalar is in [1, n-1] on import. */ +static psa_status_t wolfpsa_ecc_check_private_key( + const psa_key_attributes_t* attr, const uint8_t* data, size_t data_length) +{ + ecc_key ecc; + int curve_id; + int ret; + + curve_id = wc_psa_get_ecc_curve_id(attr->type, attr->bits); + if (curve_id == ECC_CURVE_INVALID) { + return PSA_ERROR_NOT_SUPPORTED; + } + ret = wc_ecc_init_ex(&ecc, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_ecc_import_private_key_ex(data, (word32)data_length, NULL, 0, + &ecc, curve_id); + wc_ecc_free(&ecc); + } + + if (ret == MEMORY_E) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + return (ret == 0) ? PSA_SUCCESS : PSA_ERROR_INVALID_ARGUMENT; +} +#endif /* HAVE_ECC */ + static psa_status_t wolfpsa_infer_key_bits(psa_key_attributes_t* attr, const uint8_t* data, size_t data_length) @@ -603,43 +674,6 @@ static psa_status_t wolfpsa_infer_key_bits(psa_key_attributes_t* attr, return PSA_SUCCESS; } -#ifndef NO_RSA - if (PSA_KEY_TYPE_IS_RSA(attr->type)) { - RsaKey rsa; - word32 idx = 0; - int ret; - int size; - - /* Parse-only key: no devId, because wc_RsaEncryptSize() answers a - * hard-coded 2048 bits for a device that declines the call, which - * would let a zero-modulus key past the size check below. */ - ret = wc_InitRsaKey(&rsa, NULL); - if (ret != 0) { - return psa_wc_error_to_psa_status(ret); - } - - if (attr->type == PSA_KEY_TYPE_RSA_PUBLIC_KEY) { - ret = wc_RsaPublicKeyDecode(data, &idx, &rsa, (word32)data_length); - } - else { - ret = wc_RsaPrivateKeyDecode(data, &idx, &rsa, (word32)data_length); - } - - if (ret == 0) { - size = wc_RsaEncryptSize(&rsa); - if (size <= 0) { - ret = BAD_FUNC_ARG; - } - else { - attr->bits = (psa_key_bits_t)((size_t)size * 8U); - } - } - - wc_FreeRsaKey(&rsa); - return ret == 0 ? PSA_SUCCESS : psa_wc_error_to_psa_status(ret); - } -#endif - if (PSA_KEY_TYPE_IS_DH(attr->type)) { attr->bits = (psa_key_bits_t)(data_length * 8U); return PSA_SUCCESS; @@ -992,6 +1026,8 @@ static psa_status_t psa_key_attributes_deserialize( XMEMCPY(&attributes->policy.alg, buffer, sizeof(psa_algorithm_t)); buffer += sizeof(psa_algorithm_t); + /* Persistent keys have no enrollment algorithm */ + attributes->policy.alg2 = PSA_ALG_NONE; XMEMCPY(&attributes->lifetime, buffer, sizeof(psa_key_lifetime_t)); @@ -1112,9 +1148,11 @@ psa_status_t psa_import_key( size_t buffer_size; size_t attr_length; int ret = 0; + int alg2_ok = 0; void* store = NULL; psa_key_attributes_t attr; + wolfpsa_clear_key_id(key_id); /* Check parameters */ if (attributes == NULL || data == NULL || key_id == NULL) { wolfpsa_debug_import_reason("invalid parameters", attributes, @@ -1122,9 +1160,6 @@ psa_status_t psa_import_key( return PSA_ERROR_INVALID_ARGUMENT; } - /* Always treat key_id as output-only. */ - *key_id = PSA_KEY_ID_NULL; - /* A zero-length blob is not a valid key of any type. Reject it up front: * the bits-inference path only rejects it when bits are not supplied, and * RSA/ECC/DH have no per-type length check, so a zero-length import with a @@ -1157,8 +1192,12 @@ psa_status_t psa_import_key( return PSA_ERROR_NOT_SUPPORTED; } - if (attr.policy.alg2 != PSA_ALG_NONE) { - wolfpsa_debug_import_reason("unsupported secondary algorithm", &attr, +#ifdef WOLFPSA_ENROLLMENT_ALG + /* The persistent key format has no field for the enrollment algorithm */ + alg2_ok = PSA_KEY_LIFETIME_IS_VOLATILE(attr.lifetime); +#endif /* WOLFPSA_ENROLLMENT_ALG */ + if (attr.policy.alg2 != PSA_ALG_NONE && !alg2_ok) { + wolfpsa_debug_import_reason("unsupported enrollment algorithm", &attr, data_length); return PSA_ERROR_NOT_SUPPORTED; } @@ -1171,6 +1210,23 @@ psa_status_t psa_import_key( wolfpsa_debug_import_reason("unsupported key type", &attr, data_length); return PSA_ERROR_NOT_SUPPORTED; } +#ifndef NO_RSA + if (PSA_KEY_TYPE_IS_RSA(attr.type)) { + psa_key_bits_t rsa_bits = 0; + + status = wolfpsa_rsa_key_bits(attr.type, data, data_length, + &rsa_bits); + if (status == PSA_SUCCESS && attr.bits != 0 && attr.bits != rsa_bits) { + status = PSA_ERROR_INVALID_ARGUMENT; + } + if (status != PSA_SUCCESS) { + wolfpsa_debug_import_reason("invalid RSA key data", &attr, + data_length); + return status; + } + attr.bits = rsa_bits; + } +#endif /* !NO_RSA */ if (attr.bits == 0) { status = wolfpsa_infer_key_bits(&attr, data, data_length); if (status != PSA_SUCCESS) { @@ -1417,6 +1473,18 @@ psa_status_t psa_import_key( data_length); return PSA_ERROR_INVALID_ARGUMENT; } +#ifdef HAVE_ECC + if (PSA_KEY_TYPE_IS_ECC_KEY_PAIR(attr.type) && + family != PSA_ECC_FAMILY_MONTGOMERY && + family != PSA_ECC_FAMILY_TWISTED_EDWARDS) { + status = wolfpsa_ecc_check_private_key(&attr, data, data_length); + if (status != PSA_SUCCESS) { + wolfpsa_debug_import_reason("invalid ECC private key", &attr, + data_length); + return status; + } + } +#endif /* HAVE_ECC */ } /* Check if the key storage is initialized */ @@ -1579,6 +1647,7 @@ psa_status_t psa_generate_key( uint8_t *key_data = NULL; size_t key_data_length = 0; + wolfpsa_clear_key_id(key_id); if (attributes == NULL || key_id == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -1887,6 +1956,7 @@ psa_status_t psa_export_key( int ret; void* store = NULL; + wolfpsa_clear_length(data_length); /* Check parameters: a NULL data pointer is only an error when the * caller declared a nonzero capacity; (NULL, 0) is a valid * zero-capacity output buffer that must reach the size checks below. */ @@ -1979,7 +2049,6 @@ psa_status_t psa_export_key( /* A short/failed read may have left partial key material in the * caller-owned buffer; zeroize it like the sibling read paths. */ wc_ForceZero(data, key_data_length); - *data_length = 0; return PSA_ERROR_STORAGE_FAILURE; } *data_length = key_data_length; @@ -2007,6 +2076,7 @@ psa_status_t psa_export_public_key( void* store = NULL; int use_volatile = 0; + wolfpsa_clear_length(data_length); /* A NULL data pointer is only an error when the caller declared a * nonzero capacity; (NULL, 0) must reach the size checks below. */ if (data_length == NULL || (data == NULL && data_size != 0)) { @@ -2368,6 +2438,7 @@ psa_status_t psa_get_key_attributes( if (attributes == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } + psa_reset_key_attributes(attributes); /* Check if the key storage is initialized */ status = psa_key_storage_check_init(); @@ -2565,6 +2636,18 @@ static int wolfpsa_alg_intersect(psa_algorithm_t src_alg, return ok; } +/* As wolfpsa_alg_intersect(), but alg2 unset on either side is dropped */ +static int wolfpsa_alg2_intersect(psa_algorithm_t src_alg, + psa_algorithm_t dst_alg, + psa_algorithm_t* alg) +{ + if (src_alg == PSA_ALG_NONE || dst_alg == PSA_ALG_NONE) { + *alg = PSA_ALG_NONE; + return 1; + } + return wolfpsa_alg_intersect(src_alg, dst_alg, alg); +} + /* Copy a key in the PSA key storage */ psa_status_t psa_copy_key( psa_key_id_t source_key, @@ -2583,16 +2666,14 @@ psa_status_t psa_copy_key( psa_key_attributes_t src_attr = PSA_KEY_ATTRIBUTES_INIT; psa_key_attributes_t dst_attr; psa_algorithm_t copy_alg = PSA_ALG_NONE; + psa_algorithm_t copy_alg2 = PSA_ALG_NONE; + wolfpsa_clear_key_id(target_key); /* Check parameters */ if (attributes == NULL || target_key == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } - /* The API guarantees PSA_KEY_ID_NULL on failure, so clear the output - * before any fallible operation below. */ - *target_key = PSA_KEY_ID_NULL; - /* Check if the key storage is initialized */ status = psa_key_storage_check_init(); if (status != PSA_SUCCESS) { @@ -2626,7 +2707,10 @@ psa_status_t psa_copy_key( } if (!wolfpsa_alg_intersect(psa_get_key_algorithm(&vol_attr), - attributes->policy.alg, ©_alg)) { + attributes->policy.alg, ©_alg) || + !wolfpsa_alg2_intersect(vol_attr.policy.alg2, + attributes->policy.alg2, + ©_alg2)) { wolfpsa_forcezero_free_key_data(key_data, key_data_length); return PSA_ERROR_INVALID_ARGUMENT; } @@ -2642,6 +2726,7 @@ psa_status_t psa_copy_key( dst_attr.policy.usage = psa_get_key_usage_flags(&vol_attr) & psa_get_key_usage_flags(&dst_attr); dst_attr.policy.alg = copy_alg; + dst_attr.policy.alg2 = copy_alg2; status = psa_import_key(&dst_attr, key_data, key_data_length, target_key); @@ -2697,7 +2782,9 @@ psa_status_t psa_copy_key( } if (!wolfpsa_alg_intersect(psa_get_key_algorithm(&src_attr), - attributes->policy.alg, ©_alg)) { + attributes->policy.alg, ©_alg) || + !wolfpsa_alg2_intersect(src_attr.policy.alg2, attributes->policy.alg2, + ©_alg2)) { wolfPSA_Store_Close(store); return PSA_ERROR_INVALID_ARGUMENT; } @@ -2713,6 +2800,7 @@ psa_status_t psa_copy_key( dst_attr.policy.usage = psa_get_key_usage_flags(&src_attr) & psa_get_key_usage_flags(&dst_attr); dst_attr.policy.alg = copy_alg; + dst_attr.policy.alg2 = copy_alg2; XMEMCPY(&key_data_length, header + attr_length, sizeof(size_t)); status = wolfpsa_validate_stored_key_data_length(key_data_length); @@ -2742,6 +2830,32 @@ psa_status_t psa_copy_key( return status; } +/* Exact match, or the ANY_HASH wildcard of a hash-and-sign family */ +static int wolfpsa_policy_alg_permits(psa_algorithm_t key_alg, + psa_algorithm_t alg) +{ + if (key_alg == PSA_ALG_NONE) { + return 0; + } + if (key_alg == alg) { + return 1; + } + return PSA_ALG_IS_SIGN_HASH(alg) && + PSA_ALG_SIGN_GET_HASH(key_alg) == PSA_ALG_ANY_HASH && + PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_ANY_HASH && + (key_alg & ~PSA_ALG_HASH_MASK) == (alg & ~PSA_ALG_HASH_MASK); +} + +/* Whether the permitted algorithm or the enrollment algorithm allows alg */ +int wolfpsa_key_permits_alg(const psa_key_attributes_t* attributes, + psa_algorithm_t alg) +{ + return wolfpsa_policy_alg_permits(psa_get_key_algorithm(attributes), + alg) || + wolfpsa_policy_alg_permits( + psa_get_key_enrollment_algorithm(attributes), alg); +} + /* Check whether a key supports a given algorithm and usage combination. * * PSA Crypto API 1.4 semantics: @@ -2759,7 +2873,6 @@ psa_status_t psa_check_key_usage(psa_key_id_t key, psa_status_t status; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; wolfpsa_trace("psa_check_key_usage(key=%u alg=0x%08x usage=0x%08x)", (unsigned)key, (unsigned)alg, (unsigned)usage); @@ -2775,35 +2888,9 @@ psa_status_t psa_check_key_usage(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - if (alg != PSA_ALG_NONE) { - key_alg = psa_get_key_algorithm(&attributes); - - /* A key with no permitted algorithm cannot be used for any - * cryptographic operation. */ - if (key_alg == PSA_ALG_NONE) { - psa_reset_key_attributes(&attributes); - return PSA_ERROR_NOT_PERMITTED; - } - - /* Exact match is always accepted. Otherwise apply the ANY_HASH - * wildcard rule: a sign-hash policy using PSA_ALG_ANY_HASH authorises - * any concrete hash-and-sign algorithm of the same base family. */ - if (key_alg != alg) { - int alg_ok = 0; - - if (PSA_ALG_IS_SIGN_HASH(alg) && - PSA_ALG_SIGN_GET_HASH(key_alg) == PSA_ALG_ANY_HASH && - PSA_ALG_SIGN_GET_HASH(alg) != PSA_ALG_ANY_HASH && - ((key_alg & ~PSA_ALG_HASH_MASK) == (alg & ~PSA_ALG_HASH_MASK))) - { - alg_ok = 1; - } - - if (!alg_ok) { - psa_reset_key_attributes(&attributes); - return PSA_ERROR_NOT_PERMITTED; - } - } + if (alg != PSA_ALG_NONE && !wolfpsa_key_permits_alg(&attributes, alg)) { + psa_reset_key_attributes(&attributes); + return PSA_ERROR_NOT_PERMITTED; } psa_reset_key_attributes(&attributes); diff --git a/src/psa_key_wrap.c b/src/psa_key_wrap.c index 3a66794..44340ec 100644 --- a/src/psa_key_wrap.c +++ b/src/psa_key_wrap.c @@ -29,6 +29,7 @@ #include #include "psa_trace.h" +#include "psa_output.h" #include #include #include @@ -54,7 +55,6 @@ psa_status_t psa_wrap_key(psa_key_id_t wrapping_key, psa_key_attributes_t target_attr; psa_key_type_t wrap_type; psa_key_usage_t wrap_usage; - psa_algorithm_t wrap_alg; uint8_t *kek = NULL; size_t kek_len = 0; uint8_t *plaintext = NULL; @@ -66,6 +66,7 @@ psa_status_t psa_wrap_key(psa_key_id_t wrapping_key, wolfpsa_trace("psa_wrap_key(wrapping_key=%u alg=0x%08x key=%u)", (unsigned)wrapping_key, (unsigned)alg, (unsigned)key); + wolfpsa_clear_length(data_length); /* Validate output pointers */ if (data == NULL || data_length == NULL) { return PSA_ERROR_INVALID_ARGUMENT; @@ -105,8 +106,7 @@ psa_status_t psa_wrap_key(psa_key_id_t wrapping_key, return PSA_ERROR_NOT_PERMITTED; } - wrap_alg = psa_get_key_algorithm(&wrap_attr); - if (wrap_alg == PSA_ALG_NONE || wrap_alg != alg) { + if (!wolfpsa_key_permits_alg(&wrap_attr, alg)) { wolfpsa_forcezero_free_key_data(kek, kek_len); return PSA_ERROR_NOT_PERMITTED; } @@ -206,7 +206,6 @@ psa_status_t psa_unwrap_key(const psa_key_attributes_t *attributes, psa_key_attributes_t wrap_attr; psa_key_type_t wrap_type; psa_key_usage_t wrap_usage; - psa_algorithm_t wrap_alg; uint8_t *kek = NULL; size_t kek_len = 0; uint8_t *plaintext = NULL; @@ -217,13 +216,12 @@ psa_status_t psa_unwrap_key(const psa_key_attributes_t *attributes, wolfpsa_trace("psa_unwrap_key(wrapping_key=%u alg=0x%08x data_len=%zu)", (unsigned)wrapping_key, (unsigned)alg, data_length); + wolfpsa_clear_key_id(key); /* Validate pointers */ if (attributes == NULL || data == NULL || key == NULL) { return PSA_ERROR_INVALID_ARGUMENT; } - *key = PSA_KEY_ID_NULL; - /* Only key-wrap category algorithms are accepted */ if (!PSA_ALG_IS_KEY_WRAP(alg)) { return PSA_ERROR_INVALID_ARGUMENT; @@ -266,8 +264,7 @@ psa_status_t psa_unwrap_key(const psa_key_attributes_t *attributes, return PSA_ERROR_NOT_PERMITTED; } - wrap_alg = psa_get_key_algorithm(&wrap_attr); - if (wrap_alg == PSA_ALG_NONE || wrap_alg != alg) { + if (!wolfpsa_key_permits_alg(&wrap_attr, alg)) { wolfpsa_forcezero_free_key_data(kek, kek_len); return PSA_ERROR_NOT_PERMITTED; } diff --git a/src/psa_mac.c b/src/psa_mac.c index 712a532..37557c3 100644 --- a/src/psa_mac.c +++ b/src/psa_mac.c @@ -29,6 +29,7 @@ #include #include "psa_size.h" +#include "psa_output.h" #include #include #include @@ -140,11 +141,45 @@ static int wolfpsa_hash_type_from_alg(psa_algorithm_t alg) return WC_HASH_TYPE_SHA512_224; case PSA_ALG_SHA_512_256: return WC_HASH_TYPE_SHA512_256; + case PSA_ALG_SHA3_224: + return WC_HASH_TYPE_SHA3_224; + case PSA_ALG_SHA3_256: + return WC_HASH_TYPE_SHA3_256; + case PSA_ALG_SHA3_384: + return WC_HASH_TYPE_SHA3_384; + case PSA_ALG_SHA3_512: + return WC_HASH_TYPE_SHA3_512; default: return WC_HASH_TYPE_NONE; } } +/* Whether permitted algorithm key_alg allows MAC alg and its length */ +static int wolfpsa_mac_alg_permitted(const psa_key_attributes_t *attributes, + psa_algorithm_t key_alg, + psa_algorithm_t alg) +{ + size_t req_length; + size_t min_length; + + if (key_alg == PSA_ALG_NONE || + PSA_ALG_FULL_LENGTH_MAC(key_alg) != PSA_ALG_FULL_LENGTH_MAC(alg)) { + return 0; + } + + req_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, alg); + min_length = PSA_MAC_TRUNCATED_LENGTH(key_alg); + if (min_length == 0) { + min_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, + PSA_ALG_FULL_LENGTH_MAC(key_alg)); + } + + if ((key_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { + return req_length >= min_length; + } + return req_length == min_length; +} + static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, psa_key_usage_t usage, psa_algorithm_t alg, @@ -154,12 +189,6 @@ static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, { psa_status_t status; psa_key_usage_t key_usage; - psa_algorithm_t key_alg; - psa_algorithm_t key_alg_full; - psa_algorithm_t req_alg_full; - size_t req_mac_length; - size_t key_full_length; - size_t key_min_length; status = wolfpsa_get_key_data(key, attributes, key_data, key_data_length); if (status != PSA_SUCCESS) { @@ -203,57 +232,17 @@ static psa_status_t wolfpsa_mac_check_key(psa_key_id_t key, return PSA_ERROR_NOT_PERMITTED; } - key_alg = psa_get_key_algorithm(attributes); - if (key_alg == PSA_ALG_NONE) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - - /* Algorithm match checks */ - key_alg_full = PSA_ALG_FULL_LENGTH_MAC(key_alg); - req_alg_full = PSA_ALG_FULL_LENGTH_MAC(alg); - - if (key_alg_full != req_alg_full) { + if (!wolfpsa_mac_alg_permitted(attributes, + psa_get_key_algorithm(attributes), alg) && + !wolfpsa_mac_alg_permitted(attributes, + psa_get_key_enrollment_algorithm(attributes), + alg)) { wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); *key_data = NULL; *key_data_length = 0; return PSA_ERROR_NOT_PERMITTED; } - req_mac_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, alg); - key_full_length = PSA_MAC_LENGTH(attributes->type, attributes->bits, key_alg_full); - key_min_length = PSA_MAC_TRUNCATED_LENGTH(key_alg); - if (key_min_length == 0) { - key_min_length = key_full_length; - } - - if ((key_alg & PSA_ALG_MAC_AT_LEAST_THIS_LENGTH_FLAG) != 0) { - if (req_mac_length < key_min_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else { - if ((key_alg & PSA_ALG_MAC_TRUNCATION_MASK) != 0) { - if (req_mac_length != key_min_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - else if (req_mac_length != key_full_length) { - wolfpsa_forcezero_free_key_data(*key_data, *key_data_length); - *key_data = NULL; - *key_data_length = 0; - return PSA_ERROR_NOT_PERMITTED; - } - } - return PSA_SUCCESS; } @@ -520,6 +509,7 @@ psa_status_t psa_mac_sign_finish(psa_mac_operation_t *operation, wolfpsa_mac_ctx_t *ctx = wolfpsa_mac_get_ctx(operation); psa_status_t status; + wolfpsa_clear_length(mac_length); if (ctx == NULL) { return wolfpsa_mac_fail(operation, PSA_ERROR_BAD_STATE); } @@ -623,6 +613,7 @@ psa_status_t psa_mac_compute(psa_key_id_t key, psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; psa_status_t status; + wolfpsa_clear_length(mac_length); status = psa_mac_sign_setup(&operation, key, alg); if (status != PSA_SUCCESS) { return status; diff --git a/src/psa_output.h b/src/psa_output.h new file mode 100644 index 0000000..6b2a417 --- /dev/null +++ b/src/psa_output.h @@ -0,0 +1,43 @@ +/* psa_output.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 WOLFPSA_OUTPUT_H +#define WOLFPSA_OUTPUT_H + +#include + +#include + +static inline void wolfpsa_clear_length(size_t* length) +{ + if (length != NULL) { + *length = 0; + } +} + +static inline void wolfpsa_clear_key_id(psa_key_id_t* key) +{ + if (key != NULL) { + *key = PSA_KEY_ID_NULL; + } +} + +#endif /* WOLFPSA_OUTPUT_H */ diff --git a/src/psa_rsa.c b/src/psa_rsa.c index 418981d..261f9e2 100644 --- a/src/psa_rsa.c +++ b/src/psa_rsa.c @@ -93,6 +93,36 @@ static int wc_psa_get_mgf(int hash_type) } #endif /* WC_RSA_PSS || WOLFSSL_RSA_OAEP */ +/* PSA requires the hash to be as long as the one the algorithm names. Raw + * PKCS#1 v1.5 signs any input. */ +static int wc_psa_rsa_hash_length_ok(psa_algorithm_t alg, size_t hash_length) +{ + size_t expected; + + if (alg == PSA_ALG_RSA_PKCS1V15_SIGN_RAW) { + return 1; + } + expected = PSA_HASH_LENGTH(PSA_ALG_SIGN_GET_HASH(alg)); + return expected != 0 && hash_length == expected; +} + +/* Status for a failed RSA operation. Callers check the output buffer size + * first, so a buffer, padding or range error refers to the input. */ +static psa_status_t wc_psa_rsa_status(int ret, psa_status_t bad_input) +{ + switch (ret) { + case BUFFER_E: + case RSA_BUFFER_E: + case BAD_PADDING_E: + case RSA_PAD_E: + case PSS_SALTLEN_E: + case RSA_OUT_OF_RANGE_E: + return bad_input; + default: + return wc_error_to_psa_status(ret); + } +} + #ifdef WC_RSA_PSS static int wc_psa_rsa_pss_check_padding(const byte* hash, word32 hash_length, const byte* encoded, word32 encoded_length, @@ -136,7 +166,6 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, int padding; int hash_type; int hash_oid; - size_t expected_hash_len; byte* sig_input = NULL; word32 sig_input_len = 0; word32 sig_input_alloc_len = 0; @@ -150,7 +179,8 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, } if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || (wolfpsa_check_word32_length(hash_length) != PSA_SUCCESS) || - (wolfpsa_check_word32_length(signature_size) != PSA_SUCCESS)) { + (wolfpsa_check_word32_length(signature_size) != PSA_SUCCESS) || + !wc_psa_rsa_hash_length_ok(alg, hash_length)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -194,18 +224,12 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, wc_FreeRsaKey(&rsa_key); return PSA_ERROR_NOT_SUPPORTED; } - /* PSA requires the digest to be exactly as long as the hash - * named by the algorithm; anything else is INVALID_ARGUMENT. - * wc_EncodeSignature() would otherwise happily wrap a 20-byte - * digest in a SHA-256 DigestInfo, and since the verify path - * re-encodes the same way that malformed structure would - * round-trip to success in violation of RFC 8017 A.2.4. The - * WC_MAX_DIGEST_SIZE term is retained as the buffer - * precondition: wc_EncodeSignature() does not bound its own - * write, and the two constants can diverge. */ - expected_hash_len = PSA_HASH_LENGTH(PSA_ALG_SIGN_GET_HASH(alg)); - if (expected_hash_len == 0 || hash_length != expected_hash_len || - hash_length > WC_MAX_DIGEST_SIZE) { + /* The hash length is checked above, so wc_EncodeSignature() + * cannot wrap a 20-byte digest in a SHA-256 DigestInfo. This is + * the buffer precondition: wc_EncodeSignature() does not bound + * its own write, and WC_MAX_DIGEST_SIZE and PSA_HASH_LENGTH() + * can diverge. */ + if (hash_length > WC_MAX_DIGEST_SIZE) { wc_FreeRng(&rng); wc_FreeRsaKey(&rsa_key); return PSA_ERROR_INVALID_ARGUMENT; @@ -256,7 +280,7 @@ psa_status_t psa_asymmetric_sign_rsa(psa_key_type_t key_type, wc_FreeRsaKey(&rsa_key); if (ret < 0) { - return wc_error_to_psa_status(ret); + return wc_psa_rsa_status(ret, PSA_ERROR_INVALID_ARGUMENT); } *signature_length = (size_t)ret; @@ -280,7 +304,6 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, word32 idx = 0; int padding; int hash_type; - size_t expected_hash_len; (void)key_bits; @@ -291,7 +314,8 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, } if ((wolfpsa_check_word32_length(key_buffer_size) != PSA_SUCCESS) || (wolfpsa_check_word32_length(hash_length) != PSA_SUCCESS) || - (wolfpsa_check_word32_length(signature_length) != PSA_SUCCESS)) { + (wolfpsa_check_word32_length(signature_length) != PSA_SUCCESS) || + !wc_psa_rsa_hash_length_ok(alg, hash_length)) { return PSA_ERROR_INVALID_ARGUMENT; } @@ -314,6 +338,10 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, wc_FreeRsaKey(&rsa_key); return wc_error_to_psa_status(ret); } + if (signature_length != (size_t)wc_RsaEncryptSize(&rsa_key)) { + wc_FreeRsaKey(&rsa_key); + return PSA_ERROR_INVALID_SIGNATURE; + } /* Get padding type and hash type */ padding = wc_psa_get_rsa_padding(alg); @@ -359,15 +387,8 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, wc_FreeRsaKey(&rsa_key); return PSA_ERROR_NOT_SUPPORTED; } - /* Same bound as the sign path: PSA requires the digest to match - * the length of the hash named by the algorithm, and the - * WC_MAX_DIGEST_SIZE term is the buffer precondition for the - * unbounded write wc_EncodeSignature() performs. Accepting a - * mismatched length here would let a DigestInfo whose OID and - * digest length disagree verify successfully. */ - expected_hash_len = PSA_HASH_LENGTH(PSA_ALG_SIGN_GET_HASH(alg)); - if (expected_hash_len == 0 || hash_length != expected_hash_len || - hash_length > WC_MAX_DIGEST_SIZE) { + /* Same buffer precondition as the sign path */ + if (hash_length > WC_MAX_DIGEST_SIZE) { wc_ForceZero(decoded, sizeof(decoded)); wc_FreeRsaKey(&rsa_key); return PSA_ERROR_INVALID_ARGUMENT; @@ -452,13 +473,7 @@ psa_status_t psa_asymmetric_verify_rsa(psa_key_type_t key_type, wc_FreeRsaKey(&rsa_key); if (ret < 0) { - /* The shared mapper sends RSA_PAD_E to PSA_ERROR_INVALID_PADDING for - * the decrypt path. On a verification a failed unpad is a signature - * mismatch, and psa_verify_hash does not define INVALID_PADDING. */ - if (ret == RSA_PAD_E) { - return PSA_ERROR_INVALID_SIGNATURE; - } - return wc_error_to_psa_status(ret); + return wc_psa_rsa_status(ret, PSA_ERROR_INVALID_SIGNATURE); } if ((size_t)ret != hash_length) { @@ -575,7 +590,7 @@ psa_status_t psa_asymmetric_encrypt_rsa(psa_key_type_t key_type, wc_FreeRsaKey(&rsa_key); if (ret < 0) { - return wc_error_to_psa_status(ret); + return wc_psa_rsa_status(ret, PSA_ERROR_INVALID_ARGUMENT); } *output_length = (size_t)ret; @@ -721,7 +736,7 @@ psa_status_t psa_asymmetric_decrypt_rsa(psa_key_type_t key_type, status = PSA_ERROR_BUFFER_TOO_SMALL; } else if (ret < 0) { - status = wc_error_to_psa_status(ret); + status = wc_psa_rsa_status(ret, PSA_ERROR_INVALID_PADDING); } else { if (ret > 0) { diff --git a/src/psa_tf_psa.c b/src/psa_tf_psa.c index d2391c9..23cb577 100644 --- a/src/psa_tf_psa.c +++ b/src/psa_tf_psa.c @@ -29,11 +29,23 @@ #if defined(WOLFSSL_PSA_ENGINE) +#include + #include +#include #include +#include #include +#include +#include +#include #include +/* The generated crypto config must match the build's NO_FILESYSTEM */ +#if defined(MBEDTLS_FS_IO) == defined(NO_FILESYSTEM) + #error "MBEDTLS_FS_IO must be defined exactly when NO_FILESYSTEM is not" +#endif + void mbedtls_psa_crypto_free(void) { wolfPSA_CryptoCleanup(); @@ -46,4 +58,252 @@ void mbedtls_platform_zeroize(void *buf, size_t len) } } +void mbedtls_pk_init(mbedtls_pk_context* ctx) +{ + if (ctx != NULL) { + XMEMSET(ctx, 0, sizeof(*ctx)); + } +} + +void mbedtls_pk_free(mbedtls_pk_context* ctx) +{ + if (ctx != NULL) { + mbedtls_platform_zeroize(ctx->der, ctx->der_len); + XFREE(ctx->der, NULL, DYNAMIC_TYPE_PRIVATE_KEY); + XMEMSET(ctx, 0, sizeof(*ctx)); + } +} + +enum { + PK_TYPE_NONE = 0, + PK_TYPE_RSA = 1, + PK_TYPE_ECC = 2 +}; + +/* An RSA or EC private key, traditional or PKCS #8. *type becomes PK_TYPE_* + * when it is not NULL. */ +static int PkCheckKey(const byte* der, word32 len, int* type) +{ + int ret = MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + int found = PK_TYPE_NONE; + word32 idx; +#ifndef NO_RSA + RsaKey* rsa; +#endif /* !NO_RSA */ +#ifdef HAVE_ECC + ecc_key* ecc; +#endif /* HAVE_ECC */ + +#ifndef NO_RSA + rsa = wc_NewRsaKey(NULL, INVALID_DEVID, NULL); + if (rsa == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + idx = 0; + if (wc_RsaPrivateKeyDecode(der, &idx, rsa, len) == 0) { + found = PK_TYPE_RSA; + ret = 0; + } + wc_DeleteRsaKey(rsa, &rsa); +#endif /* !NO_RSA */ +#ifdef HAVE_ECC + if (ret != 0) { + ecc = wc_ecc_key_new(NULL); + if (ecc == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + idx = 0; + if (wc_EccPrivateKeyDecode(der, &idx, ecc, len) == 0) { + found = PK_TYPE_ECC; + ret = 0; + } + wc_ecc_key_free(ecc); + } +#endif /* HAVE_ECC */ + if (type != NULL) { + *type = found; + } + (void)der; + (void)len; + (void)idx; + return ret; +} + +/* PEM or DER. Encrypted keys are not supported, so pwd is not used. */ +int mbedtls_pk_parse_key(mbedtls_pk_context* ctx, const unsigned char* key, + size_t keylen, const unsigned char* pwd, size_t pwdlen) +{ + int ret; + + (void)pwd; + (void)pwdlen; + + if (ctx == NULL || ctx->der != NULL || key == NULL || + keylen > (size_t)INT_MAX) { + return PSA_ERROR_INVALID_ARGUMENT; + } + if (keylen == 0) { + return MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + + /* Big enough for the DER of a PEM key */ + ctx->der = (unsigned char*)XMALLOC(keylen, NULL, DYNAMIC_TYPE_PRIVATE_KEY); + if (ctx->der == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } +#ifdef WOLFSSL_PEM_TO_DER + ret = wc_KeyPemToDer(key, (int)keylen, ctx->der, (int)keylen, NULL); +#else + ret = WC_NO_ERR_TRACE(ASN_NO_PEM_HEADER); +#endif /* WOLFSSL_PEM_TO_DER */ + if (ret > 0) { + ctx->der_len = (size_t)ret; + ret = 0; + } + else if (ret == WC_NO_ERR_TRACE(ASN_NO_PEM_HEADER)) { + XMEMCPY(ctx->der, key, keylen); + ctx->der_len = keylen; + ret = 0; + } + else { + ret = (ret == WC_NO_ERR_TRACE(MEMORY_E)) ? + PSA_ERROR_INSUFFICIENT_MEMORY : MBEDTLS_ERR_PK_KEY_INVALID_FORMAT; + } + + if (ret == 0) { + ret = PkCheckKey(ctx->der, (word32)ctx->der_len, NULL); + } + if (ret != 0) { + mbedtls_pk_free(ctx); + } + return ret; +} + +/* The key pair signs and verifies; an RSA key also encrypts and decrypts */ +int mbedtls_pk_can_do_psa(const mbedtls_pk_context* pk, psa_algorithm_t alg, + psa_key_usage_t usage) +{ + const int sign = (usage == PSA_KEY_USAGE_SIGN_HASH || + usage == PSA_KEY_USAGE_VERIFY_HASH); + const int crypt = (usage == PSA_KEY_USAGE_ENCRYPT || + usage == PSA_KEY_USAGE_DECRYPT); + int type = PK_TYPE_NONE; + + if (pk == NULL || pk->der == NULL || + PkCheckKey(pk->der, (word32)pk->der_len, &type) != 0) { + return 0; + } + if (type == PK_TYPE_RSA) { + return (sign && (PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) || + PSA_ALG_IS_RSA_PSS(alg))) || + (crypt && (alg == PSA_ALG_RSA_PKCS1V15_CRYPT || + PSA_ALG_IS_RSA_OAEP(alg))); + } + return type == PK_TYPE_ECC && sign && PSA_ALG_IS_ECDSA(alg); +} + +#if defined(MBEDTLS_FS_IO) +int mbedtls_pk_parse_keyfile(mbedtls_pk_context* ctx, const char* path, + const char* password) +{ + byte* buf = NULL; + size_t len = 0; + int ret; + + if (wc_FileLoad(path, &buf, &len, NULL) != 0) { + return MBEDTLS_ERR_PK_FILE_IO_ERROR; + } + ret = mbedtls_pk_parse_key(ctx, buf, len, (const unsigned char*)password, + (password == NULL) ? 0 : XSTRLEN(password)); + mbedtls_platform_zeroize(buf, len); + XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} +#endif /* MBEDTLS_FS_IO */ + +#if defined(MBEDTLS_THREADING_PTHREAD) +static int ThreadingRet(int posixRet) +{ + return (posixRet == 0) ? 0 : MBEDTLS_ERR_THREADING_USAGE_ERROR; +} + +void mbedtls_mutex_init(mbedtls_threading_mutex_t* mutex) +{ + if (mutex != NULL) { + mutex->initialized = + (char)(pthread_mutex_init(&mutex->mutex, NULL) == 0); + } +} + +/* Does nothing for a zeroed or freed mutex, as in TF-PSA-Crypto */ +void mbedtls_mutex_free(mbedtls_threading_mutex_t* mutex) +{ + if (mutex != NULL && mutex->initialized) { + (void)pthread_mutex_destroy(&mutex->mutex); + mutex->initialized = 0; + } +} + +int mbedtls_mutex_lock(mbedtls_threading_mutex_t* mutex) +{ + if (mutex == NULL || !mutex->initialized) { + return MBEDTLS_ERR_THREADING_USAGE_ERROR; + } + return ThreadingRet(pthread_mutex_lock(&mutex->mutex)); +} + +int mbedtls_mutex_unlock(mbedtls_threading_mutex_t* mutex) +{ + if (mutex == NULL || !mutex->initialized) { + return MBEDTLS_ERR_THREADING_USAGE_ERROR; + } + return ThreadingRet(pthread_mutex_unlock(&mutex->mutex)); +} + +int mbedtls_condition_variable_init( + mbedtls_threading_condition_variable_t* cond) +{ + if (cond == NULL) { + return MBEDTLS_ERR_THREADING_USAGE_ERROR; + } + return ThreadingRet(pthread_cond_init(&cond->cond, NULL)); +} + +void mbedtls_condition_variable_free( + mbedtls_threading_condition_variable_t* cond) +{ + if (cond != NULL) { + (void)pthread_cond_destroy(&cond->cond); + } +} + +int mbedtls_condition_variable_signal( + mbedtls_threading_condition_variable_t* cond) +{ + if (cond == NULL) { + return MBEDTLS_ERR_THREADING_USAGE_ERROR; + } + return ThreadingRet(pthread_cond_signal(&cond->cond)); +} + +int mbedtls_condition_variable_broadcast( + mbedtls_threading_condition_variable_t* cond) +{ + if (cond == NULL) { + return MBEDTLS_ERR_THREADING_USAGE_ERROR; + } + return ThreadingRet(pthread_cond_broadcast(&cond->cond)); +} + +int mbedtls_condition_variable_wait( + mbedtls_threading_condition_variable_t* cond, + mbedtls_threading_mutex_t* mutex) +{ + if (cond == NULL || mutex == NULL || !mutex->initialized) { + return MBEDTLS_ERR_THREADING_USAGE_ERROR; + } + return ThreadingRet(pthread_cond_wait(&cond->cond, &mutex->mutex)); +} +#endif /* MBEDTLS_THREADING_PTHREAD */ + #endif /* WOLFSSL_PSA_ENGINE */ diff --git a/test-psa-api/psa-crypto-tests.sh b/test-psa-api/psa-crypto-tests.sh index 28b112d..d5abd54 100755 --- a/test-psa-api/psa-crypto-tests.sh +++ b/test-psa-api/psa-crypto-tests.sh @@ -8,7 +8,7 @@ PATCH_FILE="${SCRIPT_DIR}/psa-arch-tests_ec_overflow.patch" TARGET_NAME="tgt_dev_apis_wolfpsa" TARGET_SRC_DIR="${SCRIPT_DIR}/${TARGET_NAME}" TARGET_DST_DIR="${PSA_ARCH_TESTS_DIR}/api-tests/platform/targets/${TARGET_NAME}" -PSA_INCLUDE_PATHS="${REPO_ROOT}/wolfpsa" +PSA_INCLUDE_PATHS="${REPO_ROOT}/wolfpsa;${REPO_ROOT}/build/include" PSA_CRYPTO_LIB_FILENAME="${REPO_ROOT}/libwolfpsa.a" PSA_STORAGE_LIB_FILENAME="" PSA_ARCH_TESTS_REPO=${PSA_ARCH_TESTS_REPO:-"https://github.com/ARM-software/psa-arch-tests.git"} diff --git a/test/Makefile b/test/Makefile index 5877f3a..32bdfa7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -35,9 +35,10 @@ CFLAGS += --coverage LDFLAGS += --coverage endif -# The ECC curve regression tests read the library's feature macros -# (they include and branch on HAVE_ECC_KOBLITZ / -# HAVE_ECC_BRAINPOOL), so only they mirror the library build: +# The ECC curve and GCM short tag regression tests read the library's +# feature macros (they include and branch on +# HAVE_ECC_KOBLITZ / HAVE_ECC_BRAINPOOL / WOLFSSL_MIN_AUTH_TAG_SZ), so only +# they mirror the library build: # -DWOLFSSL_USER_SETTINGS plus USER_SETTINGS_PATH, which takes the same # value as the library build and by default resolves to the repository # root, which ships the baseline user_settings.h. The flag and the @@ -51,11 +52,16 @@ 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 (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 ifneq ($(WOLFSSL_BUILD_DIR),) CPPFLAGS += -I$(WOLFSSL_BUILD_DIR) endif CPPFLAGS += -I$(WOLFPSA_PATH) -I$(WOLFPSA_PATH)/wolfpsa +CPPFLAGS += -I$(WOLFPSA_PATH)/$(BUILD_DIR)/include CPPFLAGS += -I$(WOLFPSA_PATH)/src CPPFLAGS += -I$(WOLFPSA_PATH)/test/psa_server @@ -107,29 +113,39 @@ PSA_REGRESSION_TESTS = psa_aead_multipart_test \ psa_ecc_curve_caps_test \ psa_xof_no_backend_test \ psa_ecc_sig_len_test \ + psa_rsa_status_test \ psa_xof_set_context_test \ psa_cipher_inplace_test \ psa_cipher_overlap_test \ psa_des3_pkcs7_test \ + psa_aead_call_order_test \ psa_eddsa_mont_export_test \ psa_eddsa_mont_gen_test \ psa_pure_eddsa_context_test \ psa_sign_hash_eddsa_test \ psa_key_infer_bits_test \ psa_cipher_oneshot_len_test \ + psa_reset_outputs_test \ psa_pqc_export_seed_test \ psa_key_declared_bits_test \ psa_store_read_open_test \ psa_store_dir_validation_test \ psa_import_zero_length_test \ + psa_import_key_data_test \ psa_copy_key_cross_lifetime_test \ - psa_zero_capacity_buffer_test + psa_zero_capacity_buffer_test \ + psa_want_surface_test \ + psa_aead_gcm_short_tag_test BINARIES += $(PSA_REGRESSION_TESTS) # TF-PSA-Crypto extension tests, built with the default link recipe. -PSA_TF_PSA_TESTS = psa_tf_psa_extra_test +PSA_TF_PSA_TESTS = psa_tf_psa_extra_test psa_tf_psa_pk_test BINARIES += $(PSA_TF_PSA_TESTS) +# The pk test reads wolfSSL's test keys +psa_tf_psa_pk_test: \ + CPPFLAGS += -DPK_CERTS_DIR='"$(abspath $(WOLFSSL_PATH))/certs"' + # Regression test with a custom link: it replaces the store backend, so it # cannot use the recipe above (see PROBE_LIB below). BINARIES += psa_import_key_probe_test @@ -183,11 +199,12 @@ psa_rsa_pss_interop_test: require-wolfssl-lib $(PSA_RSA_PSS_TEST_OBJS) $(PSA_14_TESTS): %: require-wolfssl-lib psa_server/%.o $(CC) $(CFLAGS) -o $@ psa_server/$@.o $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) -# Target-specific: only the curve tests compile with the library's -# user_settings.h (see the comment at USER_SETTINGS_PATH above). The -# value reaches the psa_server/%.o prerequisite's recipe, which is what +# Target-specific: only the curve and GCM short tag tests compile with the +# library's user_settings.h (see the comment at USER_SETTINGS_PATH above). +# The value reaches the psa_server/%.o prerequisite's recipe, which is what # needs the flag and the include path. -psa_ecc_verify_curve_test psa_ecc_ecdh_curve_test psa_ecc_curve_caps_test: \ +psa_ecc_verify_curve_test psa_ecc_ecdh_curve_test psa_ecc_curve_caps_test \ +psa_aead_gcm_short_tag_test: \ CPPFLAGS += $(WOLFSSL_CPPFLAGS) -I$(USER_SETTINGS_PATH) $(PSA_REGRESSION_TESTS) $(PSA_TF_PSA_TESTS): %: require-wolfssl-lib \ @@ -209,21 +226,27 @@ psa_tls_client: # user_settings.h with that knob enabled instead of the shared libwolfpsa. CRYPTOCB_BUILD_DIR := $(abspath .)/build-cryptocb$(if $(SANITIZE_FLAGS),-asan) CRYPTOCB_LIB := $(CRYPTOCB_BUILD_DIR)/libwolfpsa-cryptocb.a -CRYPTOCB_CPPFLAGS := -DWOLFSSL_USER_SETTINGS -DWOLF_CRYPTO_CB \ - -I$(abspath $(WOLFSSL_PATH)) -I$(WOLFPSA_ABS) -I$(WOLFPSA_ABS)/wolfpsa \ - -I$(WOLFPSA_ABS)/src +VARIANT_INC := -I$(abspath $(WOLFSSL_PATH)) -I$(WOLFPSA_ABS) \ + -I$(WOLFPSA_ABS)/wolfpsa -I$(WOLFPSA_ABS)/src +CRYPTOCB_CPPFLAGS := -DWOLFSSL_USER_SETTINGS -DWOLF_CRYPTO_CB $(VARIANT_INC) \ + -I$(CRYPTOCB_BUILD_DIR)/include # FORCE, because only the sub-make knows whether a library source changed. # Without it make treats the existing archive as permanently up to date and # the test relinks against a stale copy of the code it is meant to guard. $(CRYPTOCB_LIB): FORCE + $(call variant_lib,$(CRYPTOCB_BUILD_DIR),-DWOLF_CRYPTO_CB) + +# Build a library variant: $(1) is its build dir, $(2) its extra defines +define variant_lib $(MAKE) -C $(WOLFPSA_PATH) $@ \ LIBNAME=$@ \ - BUILD_DIR=$(CRYPTOCB_BUILD_DIR) \ + BUILD_DIR=$(1) \ WOLFSSL_PATH=$(abspath $(WOLFSSL_PATH)) \ - WOLFSSL_CPPFLAGS="-DWOLFSSL_USER_SETTINGS -DWOLF_CRYPTO_CB" \ + WOLFSSL_CPPFLAGS="-DWOLFSSL_USER_SETTINGS $(2)" \ CFLAGS="$(CFLAGS)" \ LDFLAGS="$(SANITIZE_FLAGS)" +endef FORCE: @@ -239,6 +262,20 @@ psa_kdf_zeroize_output_test: psa_server/psa_kdf_zeroize_output_test.c $(CRYPTOCB $(CC) $(CRYPTOCB_CPPFLAGS) $(CFLAGS) -o $@ $< $(CRYPTOCB_LIB) \ $(SANITIZE_FLAGS) -lpthread -lm +# The enrollment algorithm is opt-in, so its test has a library built with it +ENROLL_BUILD_DIR := $(abspath .)/build-enroll$(if $(SANITIZE_FLAGS),-asan) +ENROLL_LIB := $(ENROLL_BUILD_DIR)/libwolfpsa-enroll.a +ENROLL_CPPFLAGS := -DWOLFSSL_USER_SETTINGS -DWOLFPSA_ENROLLMENT_ALG \ + $(VARIANT_INC) -I$(ENROLL_BUILD_DIR)/include + +$(ENROLL_LIB): FORCE + $(call variant_lib,$(ENROLL_BUILD_DIR),-DWOLFPSA_ENROLLMENT_ALG) + +psa_enrollment_alg_test: psa_server/psa_enrollment_alg_test.c $(ENROLL_LIB) \ + FORCE + $(CC) $(ENROLL_CPPFLAGS) $(CFLAGS) -o $@ $< $(ENROLL_LIB) \ + $(SANITIZE_FLAGS) -lpthread -lm + # psa_import_key_probe_test ships its own wolfPSA_Store_* backend: the POSIX # store cannot fail a read probe with an I/O error, only succeed or report # "not available", so the probe-failure path needs a mock. It therefore links @@ -277,6 +314,21 @@ psa_store_commit_test: require-wolfssl-lib psa_server/psa_store_commit_test.o FO $(CC) $(CFLAGS) -o $@ psa_server/psa_store_commit_test.o \ $(WOLFPSA_PATH)/libwolfpsa.a $(LDFLAGS) -lwolfssl -lpthread -lm $(RPATH_WOLFSSL) +# The threading API exists only with MBEDTLS_THREADING_C, so its test has a +# library built with it +THREADS_BUILD_DIR := $(abspath .)/build-threads$(if $(SANITIZE_FLAGS),-asan) +THREADS_LIB := $(THREADS_BUILD_DIR)/libwolfpsa-threads.a +THREADS_CPPFLAGS := -DWOLFSSL_USER_SETTINGS -DMBEDTLS_THREADING_C \ + $(VARIANT_INC) -I$(THREADS_BUILD_DIR)/include + +$(THREADS_LIB): FORCE + $(call variant_lib,$(THREADS_BUILD_DIR),-DMBEDTLS_THREADING_C) + +psa_tf_psa_threading_test: psa_server/psa_tf_psa_threading_test.c \ + $(THREADS_LIB) FORCE + $(CC) $(THREADS_CPPFLAGS) $(CFLAGS) -o $@ $< $(THREADS_LIB) \ + $(SANITIZE_FLAGS) -pthread -lm + wolfcrypt-psa-benchmark: make -C wolfcrypt-benchmark cp wolfcrypt-benchmark/wolfcrypt-psa-benchmark ./wolfcrypt-psa-benchmark @@ -302,7 +354,8 @@ rebuild-wolfssl-psa: $(WOLFPSA_PATH)/libwolfpsa.so --with-psa-lib="$(WOLFPSA_ABS)" \ --with-psa-lib-name=wolfpsa \ CFLAGS="-O2 $(DEBUG_FLAGS) $(SANITIZE_FLAGS)" \ - CPPFLAGS="-DWOLFSSL_PSA_NO_RNG -DWOLFSSL_PSA_NO_HASH -DWOLFSSL_PSA_NO_AES" \ + CPPFLAGS="-DWOLFSSL_PSA_NO_RNG -DWOLFSSL_PSA_NO_HASH -DWOLFSSL_PSA_NO_AES \ + -I$(WOLFPSA_ABS)/$(BUILD_DIR)/include" \ LDFLAGS="$(SANITIZE_FLAGS) -Wl,-rpath,$(WOLFPSA_ABS) -Wl,-rpath-link,$(WOLFPSA_ABS)" $(MAKE) -C "$(WOLFSSL_PATH)" CFLAGS="-O2 $(DEBUG_FLAGS) $(SANITIZE_FLAGS)" LDFLAGS="$(SANITIZE_FLAGS) -Wl,-rpath,$(WOLFPSA_ABS) -Wl,-rpath-link,$(WOLFPSA_ABS)" src/libwolfssl.la @@ -315,5 +368,9 @@ clean: rm -f psa_server/*.o rm -rf $(abspath .)/build-cryptocb $(abspath .)/build-cryptocb-asan rm -rf $(abspath .)/build-probe $(abspath .)/build-probe-asan + rm -rf $(abspath .)/build-threads $(abspath .)/build-threads-asan rm -f $(BINARIES) psa_devid_cryptocb_test psa_kdf_zeroize_output_test *.o *.log + rm -f psa_tf_psa_threading_test + rm -rf $(abspath .)/build-enroll $(abspath .)/build-enroll-asan + rm -f psa_enrollment_alg_test rm -f psa_server/*.gcda psa_server/*.gcno diff --git a/test/mbedtls_compat/Makefile b/test/mbedtls_compat/Makefile new file mode 100644 index 0000000..6aaa8dc --- /dev/null +++ b/test/mbedtls_compat/Makefile @@ -0,0 +1,124 @@ +# 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 +THREADING_TEST := +ifeq ($(MBEDTLS_THREADING_C),1) +LAYER_FLAGS += -DMBEDTLS_THREADING_C +LAYER_LIBS += -pthread +THREADING_TEST := $(BUILD_DIR)/threading_test +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) $(THREADING_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 +$(THREADING_TEST): threading_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) $(THREADING_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 0000000..667cd39 --- /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 0000000..f85f1ec --- /dev/null +++ b/test/mbedtls_compat/pk_test.c @@ -0,0 +1,111 @@ +/* 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 + +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", INVALID }, + /* 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 0000000..5a03f16 --- /dev/null +++ b/test/mbedtls_compat/programs.sh @@ -0,0 +1,223 @@ +#!/bin/sh + +# Run x509_crt_test.c, pk_test.c and ssl_test.c on Mbed TLS's test data, and +# with MBEDTLS_THREADING_C=1 threading_test.c, 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 + TESTS="$TESTS threading_test" +else + echo "SKIP: threading_test and 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" +/* The CN of server5.crt */ +#define HOST_NAME "localhost" +/* Handshake steps for each side */ +#define MAX_STEPS 20 + +/* 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; + +static mbedtls_x509_crt ca; +static mbedtls_x509_crt cert; +static mbedtls_pk_context key; +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 PeerNew(Peer* p, int endpoint, int authmode, 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, &ca, NULL); + if (endpoint == MBEDTLS_SSL_IS_SERVER) { + ret = mbedtls_ssl_conf_own_cert(&p->conf, &cert, &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 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); +} + +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 }, + { "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); + 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) { + printf("FAIL: loading %s, %s and %s\n", CA_FILE, CERT_FILE, KEY_FILE); + 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_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/threading_test.c b/test/mbedtls_compat/threading_test.c new file mode 100644 index 0000000..f74624c --- /dev/null +++ b/test/mbedtls_compat/threading_test.c @@ -0,0 +1,228 @@ +/* threading_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 + */ + +/* Mutexes and condition variables through the TF-PSA-Crypto 1.x API, and PSA + * calls from several threads at once, as MBEDTLS_THREADING_C promises. */ + +#include +#include + +#include +#include + +#define THREADS 4 +#define ROUNDS 10000 +#define KEYS 100 + +static mbedtls_threading_mutex_t lock; +static mbedtls_threading_condition_variable_t ready; +static mbedtls_threading_condition_variable_t start; +static int counter; +static int waiting; +static int go; +/* A thread returns its address on failure */ +static int failed; + +static int StartThreads(pthread_t* t, void* (*f)(void*)) +{ + int n; + + for (n = 0; n < THREADS; n++) { + if (pthread_create(&t[n], NULL, f, NULL) != 0) { + break; + } + } + return n; +} + +/* All n threads started and none failed */ +static int JoinThreads(pthread_t* t, int n) +{ + void* ret; + int ok = (n == THREADS); + int i; + + for (i = 0; i < n; i++) { + if (pthread_join(t[i], &ret) != 0 || ret != NULL) { + ok = 0; + } + } + return ok; +} + +/* A zeroed or freed mutex fails, and a second free does nothing */ +static int MutexUsage(void) +{ + mbedtls_threading_mutex_t m; + int ok; + + memset(&m, 0, sizeof(m)); + ok = mbedtls_mutex_lock(&m) == MBEDTLS_ERR_THREADING_USAGE_ERROR; + mbedtls_mutex_free(&m); + mbedtls_mutex_init(&m); + ok = ok && mbedtls_mutex_lock(&m) == 0 && mbedtls_mutex_unlock(&m) == 0; + mbedtls_mutex_free(&m); + mbedtls_mutex_free(&m); + return ok && + mbedtls_mutex_unlock(&m) == MBEDTLS_ERR_THREADING_USAGE_ERROR; +} + +static void* Count(void* arg) +{ + int i; + + (void)arg; + for (i = 0; i < ROUNDS; i++) { + if (mbedtls_mutex_lock(&lock) != 0) { + return &failed; + } + counter++; + if (mbedtls_mutex_unlock(&lock) != 0) { + return &failed; + } + } + return NULL; +} + +static int Counter(void) +{ + pthread_t t[THREADS]; + + return JoinThreads(t, StartThreads(t, Count)) && + counter == THREADS * ROUNDS; +} + +/* Signals ready, then waits for the broadcast of start */ +static void* Wait(void* arg) +{ + int ret; + + (void)arg; + ret = mbedtls_mutex_lock(&lock); + if (ret == 0) { + waiting++; + ret = mbedtls_condition_variable_signal(&ready); + while (ret == 0 && !go) { + ret = mbedtls_condition_variable_wait(&start, &lock); + } + (void)mbedtls_mutex_unlock(&lock); + } + return (ret == 0) ? NULL : &failed; +} + +static int CondVar(void) +{ + pthread_t t[THREADS]; + int n; + int ret; + + n = StartThreads(t, Wait); + ret = mbedtls_mutex_lock(&lock); + if (ret == 0) { + while (ret == 0 && waiting < n) { + ret = mbedtls_condition_variable_wait(&ready, &lock); + } + go = 1; + if (ret == 0) { + ret = mbedtls_condition_variable_broadcast(&start); + } + (void)mbedtls_mutex_unlock(&lock); + } + return JoinThreads(t, n) && ret == 0; +} + +/* Volatile keys in the shared key store */ +static void* Keys(void* arg) +{ + static const unsigned char key[16] = { 0 }; + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_attributes_t got = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t id; + psa_status_t status = PSA_SUCCESS; + int i; + + (void)arg; + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attr, PSA_ALG_ECB_NO_PADDING); + psa_set_key_type(&attr, PSA_KEY_TYPE_AES); + for (i = 0; i < KEYS && status == PSA_SUCCESS; i++) { + status = psa_import_key(&attr, key, sizeof(key), &id); + if (status == PSA_SUCCESS) { + status = psa_get_key_attributes(id, &got); + if (status == PSA_SUCCESS && + psa_get_key_type(&got) != PSA_KEY_TYPE_AES) { + status = PSA_ERROR_CORRUPTION_DETECTED; + } + psa_reset_key_attributes(&got); + if (psa_destroy_key(id) != PSA_SUCCESS) { + status = PSA_ERROR_CORRUPTION_DETECTED; + } + } + } + return (status == PSA_SUCCESS) ? NULL : &failed; +} + +static int KeyStore(void) +{ + pthread_t t[THREADS]; + + return JoinThreads(t, StartThreads(t, Keys)); +} + +int main(void) +{ + static const struct { + const char* name; + int (*f)(void); + } cases[] = { + { "mutex usage errors", MutexUsage }, + { "mutex, a counter shared by the threads", Counter }, + { "condition variable, signal and broadcast", CondVar }, + { "PSA keys in each thread", KeyStore } + }; + size_t i; + int fails = 0; + + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + mbedtls_mutex_init(&lock); + if (mbedtls_condition_variable_init(&ready) != 0 || + mbedtls_condition_variable_init(&start) != 0) { + printf("FAIL: mbedtls_condition_variable_init\n"); + return 1; + } + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + if (cases[i].f()) { + printf("PASS: %s\n", cases[i].name); + } + else { + printf("FAIL: %s\n", cases[i].name); + fails++; + } + } + mbedtls_condition_variable_free(&start); + mbedtls_condition_variable_free(&ready); + mbedtls_mutex_free(&lock); + 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 0000000..ca96c21 --- /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/test/psa_server/psa_14_misc_test.c b/test/psa_server/psa_14_misc_test.c index 1add69a..11ca95f 100644 --- a/test/psa_server/psa_14_misc_test.c +++ b/test/psa_server/psa_14_misc_test.c @@ -110,8 +110,8 @@ static int test_not_supported_stubs(void) st = psa_sign_hash_complete(&sign_op, dummy_sig_out, sizeof(dummy_sig_out), &dummy_sig_len); - if (expect_status("psa_sign_hash_complete NOT_SUPPORTED", st, - PSA_ERROR_NOT_SUPPORTED) != 0) { + if (expect_status("psa_sign_hash_complete BAD_STATE", st, + PSA_ERROR_BAD_STATE) != 0) { return 1; } @@ -124,8 +124,8 @@ static int test_not_supported_stubs(void) } st = psa_verify_hash_complete(&verify_op); - if (expect_status("psa_verify_hash_complete NOT_SUPPORTED", st, - PSA_ERROR_NOT_SUPPORTED) != 0) { + if (expect_status("psa_verify_hash_complete BAD_STATE", st, + PSA_ERROR_BAD_STATE) != 0) { return 1; } @@ -136,18 +136,38 @@ static int test_not_supported_stubs(void) return 1; } + st = psa_key_agreement_iop_complete(&ka_op, &key); + if (expect_status("psa_key_agreement_iop_complete BAD_STATE", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + st = psa_generate_key_iop_setup(&gk_op, &attrs); if (expect_status("psa_generate_key_iop_setup NOT_SUPPORTED", st, PSA_ERROR_NOT_SUPPORTED) != 0) { return 1; } + st = psa_generate_key_iop_complete(&gk_op, &key); + if (expect_status("psa_generate_key_iop_complete BAD_STATE", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + st = psa_export_public_key_iop_setup(&epk_op, 0); if (expect_status("psa_export_public_key_iop_setup NOT_SUPPORTED", st, PSA_ERROR_NOT_SUPPORTED) != 0) { return 1; } + st = psa_export_public_key_iop_complete(&epk_op, dummy_sig_out, + sizeof(dummy_sig_out), + &dummy_sig_len); + if (expect_status("psa_export_public_key_iop_complete BAD_STATE", st, + PSA_ERROR_BAD_STATE) != 0) { + return 1; + } + return 0; } @@ -159,7 +179,9 @@ static int test_interruptible_abort_stubs(void) PSA_SIGN_HASH_INTERRUPTIBLE_OPERATION_INIT; psa_verify_hash_interruptible_operation_t verify_op = PSA_VERIFY_HASH_INTERRUPTIBLE_OPERATION_INIT; + psa_key_agreement_iop_t ka_op = PSA_KEY_AGREEMENT_IOP_INIT; psa_generate_key_iop_t gk_op = PSA_GENERATE_KEY_IOP_INIT; + psa_export_public_key_iop_t epk_op = PSA_EXPORT_PUBLIC_KEY_IOP_INIT; psa_status_t st; uint32_t n; @@ -171,10 +193,20 @@ static int test_interruptible_abort_stubs(void) if (expect_status("psa_verify_hash_abort", st, PSA_SUCCESS) != 0) return 1; + st = psa_key_agreement_iop_abort(&ka_op); + if (expect_status("psa_key_agreement_iop_abort", st, PSA_SUCCESS) != 0) + return 1; + st = psa_generate_key_iop_abort(&gk_op); if (expect_status("psa_generate_key_iop_abort", st, PSA_SUCCESS) != 0) return 1; + st = psa_export_public_key_iop_abort(&epk_op); + if (expect_status("psa_export_public_key_iop_abort", st, + PSA_SUCCESS) != 0) { + return 1; + } + n = psa_sign_hash_get_num_ops(&sign_op); if (n != 0) { printf("FAIL psa_sign_hash_get_num_ops=%u expected=0\n", (unsigned)n); @@ -256,9 +288,19 @@ static int test_key_derivation_output_key_custom(void) psa_key_derivation_operation_t op = PSA_KEY_DERIVATION_OPERATION_INIT; psa_custom_key_parameters_t custom = PSA_CUSTOM_KEY_PARAMETERS_INIT; psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t secret_attrs = psa_key_attributes_init(); + psa_key_id_t secret_key = 0; psa_key_id_t derived_key = 0; psa_status_t st; + psa_set_key_type(&secret_attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&secret_attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&secret_attrs, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + st = psa_import_key(&secret_attrs, secret, sizeof(secret) - 1u, + &secret_key); + if (expect_status("kdf_output_key_custom: import", st, PSA_SUCCESS) != 0) + return 1; + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); psa_set_key_bits(&attrs, 128); psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); @@ -273,8 +315,8 @@ static int test_key_derivation_output_key_custom(void) if (expect_status("kdf_output_key_custom: salt", st, PSA_SUCCESS) != 0) goto fail; - st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, - secret, sizeof(secret) - 1u); + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + secret_key); if (expect_status("kdf_output_key_custom: secret", st, PSA_SUCCESS) != 0) goto fail; @@ -292,10 +334,12 @@ static int test_key_derivation_output_key_custom(void) (void)psa_key_derivation_abort(&op); (void)psa_destroy_key(derived_key); + (void)psa_destroy_key(secret_key); return 0; fail: (void)psa_key_derivation_abort(&op); + (void)psa_destroy_key(secret_key); return 1; } @@ -1051,6 +1095,42 @@ static int test_default_devid_setter(void) return ret; } +/* Case 18: the hash of hash-and-sign algorithms; none for ECDSA_ANY, raw */ +static int test_hash_and_sign_macros(void) +{ + static const struct { + psa_algorithm_t alg; + psa_algorithm_t hash; + } cases[] = { + { PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256), PSA_ALG_SHA_256 }, + { PSA_ALG_RSA_PSS(PSA_ALG_SHA_384), PSA_ALG_SHA_384 }, + { PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_512), PSA_ALG_SHA_512 }, + { PSA_ALG_ECDSA(PSA_ALG_ANY_HASH), PSA_ALG_ANY_HASH }, + { PSA_ALG_ED25519PH, PSA_ALG_SHA_512 }, + { PSA_ALG_HASH_ML_DSA(PSA_ALG_SHA_256), PSA_ALG_SHA_256 }, + { PSA_ALG_DETERMINISTIC_HASH_ML_DSA(PSA_ALG_SHA_384), + PSA_ALG_SHA_384 }, + { PSA_ALG_ECDSA_ANY, PSA_ALG_NONE }, + { PSA_ALG_RSA_PKCS1V15_SIGN_RAW, PSA_ALG_NONE }, + { PSA_ALG_PURE_EDDSA, PSA_ALG_NONE }, + { PSA_ALG_ML_DSA, PSA_ALG_NONE }, + { PSA_ALG_SHA_256, PSA_ALG_NONE }, + }; + size_t i; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + if (PSA_ALG_IS_HASH_AND_SIGN(cases[i].alg) != + (cases[i].hash != PSA_ALG_NONE) || + PSA_ALG_SIGN_GET_HASH(cases[i].alg) != cases[i].hash) { + printf("FAIL hash-and-sign alg=0x%08x hash=0x%08x\n", + (unsigned)cases[i].alg, + (unsigned)PSA_ALG_SIGN_GET_HASH(cases[i].alg)); + return 1; + } + } + return 0; +} + int main(void) { psa_status_t st; @@ -1059,6 +1139,9 @@ int main(void) if (test_pre_init_bad_state() != 0) return 1; + if (test_interruptible_abort_stubs() != 0) + return 1; + st = psa_crypto_init(); if (st != PSA_SUCCESS) { printf("FAIL psa_crypto_init status=%d\n", (int)st); @@ -1129,6 +1212,10 @@ int main(void) if (test_default_devid_setter() != 0) return 1; + /* Case 18: PSA_ALG_IS_HASH_AND_SIGN and PSA_ALG_SIGN_GET_HASH */ + if (test_hash_and_sign_macros() != 0) + return 1; + printf("PSA 1.4 misc test: OK\n"); return 0; } diff --git a/test/psa_server/psa_aead_call_order_test.c b/test/psa_server/psa_aead_call_order_test.c new file mode 100644 index 0000000..fe8aabd --- /dev/null +++ b/test/psa_server/psa_aead_call_order_test.c @@ -0,0 +1,242 @@ +/* psa_aead_call_order_test.c + * + * Multipart AEAD accepts psa_aead_set_lengths() before or after the nonce, + * and rejects it once additional data or payload has been passed. + * + * 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 + */ + +#include +#include +#include +#include + +#define AD_LEN 8 +#define TAG_LEN 16 + +static int failures; + +static const uint8_t nonce[12] = { + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b +}; +static const uint8_t ad[AD_LEN] = { 'a', 'd', 'd', 'a', 't', 'a', '0', '1' }; +static const uint8_t msg[] = "multipart AEAD order"; + +static void expect(psa_status_t got, psa_status_t want, const char *what) +{ + if (got != want) { + printf("FAIL: %s: got %d want %d\n", what, (int)got, (int)want); + failures++; + } +} + +static psa_key_id_t import_key(psa_algorithm_t alg) +{ + static const uint8_t key_data[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, alg); + expect(psa_import_key(&attrs, key_data, sizeof(key_data), &key), + PSA_SUCCESS, "import key"); + return key; +} + +/* Multipart encrypt with the lengths set after the nonce, checked against + * the one-shot result. */ +static void check_lengths_after_nonce(psa_algorithm_t alg, const char *name) +{ + psa_aead_operation_t op = PSA_AEAD_OPERATION_INIT; + psa_key_id_t key = import_key(alg); + uint8_t want[sizeof(msg) + TAG_LEN]; + uint8_t out[sizeof(msg)]; + uint8_t tag[TAG_LEN]; + size_t want_len = 0; + size_t out_len = 0; + size_t fin_len = 0; + size_t tag_len = 0; + psa_status_t status; + + expect(psa_aead_encrypt(key, alg, nonce, sizeof(nonce), ad, sizeof(ad), + msg, sizeof(msg), want, sizeof(want), &want_len), + PSA_SUCCESS, name); + + status = psa_aead_encrypt_setup(&op, key, alg); + if (status == PSA_SUCCESS) { + status = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); + } + if (status == PSA_SUCCESS) { + status = psa_aead_set_lengths(&op, sizeof(ad), sizeof(msg)); + } + if (status == PSA_SUCCESS) { + status = psa_aead_update_ad(&op, ad, sizeof(ad)); + } + if (status == PSA_SUCCESS) { + status = psa_aead_update(&op, msg, sizeof(msg), out, sizeof(out), + &out_len); + } + if (status == PSA_SUCCESS) { + status = psa_aead_finish(&op, out + out_len, sizeof(out) - out_len, + &fin_len, tag, sizeof(tag), &tag_len); + } + expect(status, PSA_SUCCESS, name); + if (status == PSA_SUCCESS && + (want_len != out_len + fin_len + tag_len || + memcmp(want, out, out_len + fin_len) != 0 || + memcmp(want + out_len + fin_len, tag, tag_len) != 0)) { + printf("FAIL: %s: output differs from one-shot\n", name); + failures++; + } + psa_aead_abort(&op); + psa_destroy_key(key); +} + +/* CCM takes a nonce before the lengths, but no data or finish without + * them. */ +static void check_ccm_without_lengths(void) +{ + psa_aead_operation_t op = PSA_AEAD_OPERATION_INIT; + psa_key_id_t key = import_key(PSA_ALG_CCM); + uint8_t buf[TAG_LEN]; + uint8_t gen[PSA_AEAD_NONCE_MAX_SIZE]; + size_t len = 0; + size_t tag_len = 0; + + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_CCM), PSA_SUCCESS, + "CCM setup"); + expect(psa_aead_generate_nonce(&op, gen, sizeof(gen), &len), PSA_SUCCESS, + "CCM generate nonce before lengths"); + expect(psa_aead_update_ad(&op, ad, 0), PSA_ERROR_BAD_STATE, + "CCM empty additional data before lengths"); + expect(psa_aead_update_ad(&op, ad, sizeof(ad)), PSA_ERROR_BAD_STATE, + "CCM additional data before lengths"); + expect(psa_aead_set_lengths(&op, sizeof(ad), sizeof(msg)), PSA_SUCCESS, + "CCM lengths after rejected additional data"); + psa_aead_abort(&op); + + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_CCM), PSA_SUCCESS, + "CCM setup"); + expect(psa_aead_set_nonce(&op, nonce, sizeof(nonce)), PSA_SUCCESS, + "CCM nonce before lengths"); + expect(psa_aead_finish(&op, buf, sizeof(buf), &len, buf, sizeof(buf), + &tag_len), + PSA_ERROR_BAD_STATE, "CCM finish without lengths"); + psa_aead_abort(&op); + psa_destroy_key(key); +} + +/* A 12-byte CCM nonce leaves 3 bytes for the plaintext length, checked by + * whichever of the nonce and lengths is set second. */ +static void check_ccm_length_limit(void) +{ + psa_aead_operation_t op = PSA_AEAD_OPERATION_INIT; + psa_key_id_t key = import_key(PSA_ALG_CCM); + + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_CCM), PSA_SUCCESS, + "CCM setup"); + expect(psa_aead_set_nonce(&op, nonce, sizeof(nonce)), PSA_SUCCESS, + "CCM nonce"); + expect(psa_aead_set_lengths(&op, 0, 0x1000000), + PSA_ERROR_INVALID_ARGUMENT, "CCM lengths over the nonce limit"); + psa_aead_abort(&op); + + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_CCM), PSA_SUCCESS, + "CCM setup"); + expect(psa_aead_set_nonce(&op, nonce, sizeof(nonce)), PSA_SUCCESS, + "CCM nonce"); + expect(psa_aead_set_lengths(&op, 0, 0xFFFFFF), PSA_SUCCESS, + "CCM lengths at the nonce limit"); + psa_aead_abort(&op); + + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_CCM), PSA_SUCCESS, + "CCM setup"); + expect(psa_aead_set_lengths(&op, 0, 0x1000000), PSA_SUCCESS, + "CCM lengths before nonce"); + expect(psa_aead_set_nonce(&op, nonce, sizeof(nonce)), + PSA_ERROR_INVALID_ARGUMENT, "CCM nonce over the length limit"); + psa_aead_abort(&op); + psa_destroy_key(key); +} + +/* Once additional data or payload is passed, even empty, the lengths are + * fixed. */ +static void check_lengths_too_late(void) +{ + psa_aead_operation_t op = PSA_AEAD_OPERATION_INIT; + psa_key_id_t key = import_key(PSA_ALG_GCM); + uint8_t out[sizeof(msg)]; + size_t out_len = 0; + + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_GCM), PSA_SUCCESS, + "GCM setup"); + expect(psa_aead_set_nonce(&op, nonce, sizeof(nonce)), PSA_SUCCESS, + "GCM nonce"); + expect(psa_aead_update_ad(&op, ad, 0), PSA_SUCCESS, + "GCM empty additional data"); + expect(psa_aead_set_lengths(&op, 0, 0), PSA_ERROR_BAD_STATE, + "GCM lengths after additional data"); + psa_aead_abort(&op); + + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_GCM), PSA_SUCCESS, + "GCM setup"); + expect(psa_aead_set_nonce(&op, nonce, sizeof(nonce)), PSA_SUCCESS, + "GCM nonce"); + expect(psa_aead_update(&op, msg, 0, out, sizeof(out), &out_len), + PSA_SUCCESS, "GCM empty update"); + expect(psa_aead_set_lengths(&op, 0, 0), PSA_ERROR_BAD_STATE, + "GCM lengths after update"); + psa_aead_abort(&op); + +#if SIZE_MAX > UINT32_MAX + expect(psa_aead_encrypt_setup(&op, key, PSA_ALG_GCM), PSA_SUCCESS, + "GCM setup"); + expect(psa_aead_set_lengths(&op, SIZE_MAX, 0), PSA_ERROR_INVALID_ARGUMENT, + "GCM additional data length over the limit"); + expect(psa_aead_set_nonce(&op, nonce, sizeof(nonce)), PSA_ERROR_BAD_STATE, + "GCM nonce after failed lengths"); + psa_aead_abort(&op); +#endif /* SIZE_MAX > UINT32_MAX */ + psa_destroy_key(key); +} + +int main(void) +{ + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + + check_lengths_after_nonce(PSA_ALG_GCM, "GCM lengths after nonce"); + check_lengths_after_nonce(PSA_ALG_CCM, "CCM lengths after nonce"); + check_ccm_without_lengths(); + check_ccm_length_limit(); + check_lengths_too_late(); + + if (failures == 0) { + printf("AEAD call order tests: all passed\n"); + return 0; + } + printf("AEAD call order tests: %d failure(s)\n", failures); + return 1; +} diff --git a/test/psa_server/psa_aead_gcm_short_tag_test.c b/test/psa_server/psa_aead_gcm_short_tag_test.c new file mode 100644 index 0000000..b6766e7 --- /dev/null +++ b/test/psa_server/psa_aead_gcm_short_tag_test.c @@ -0,0 +1,138 @@ +/* psa_aead_gcm_short_tag_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 + */ + +/* A shortened GCM tag round trips and is verified, or is NOT_SUPPORTED + * below WOLFSSL_MIN_AUTH_TAG_SZ. test/Makefile builds this test with the + * library's user_settings.h, so the macro matches libwolfpsa. */ + +#include +#include +#include +#include + +static int failures; + +static void expect(int ok, const char* what, size_t tagLen) +{ + if (!ok) { + printf("FAIL: %s, %u byte tag\n", what, (unsigned)tagLen); + failures++; + } +} + +static void test_tag_length(psa_key_id_t key, size_t tagLen) +{ + static const uint8_t nonce[12] = { 0 }; + static const uint8_t plaintext[16] = { 0 }; + /* AES-128-GCM, all-zero key, nonce and plaintext: ciphertext, then the + * full tag */ + static const uint8_t expected[sizeof(plaintext) + 16] = { + 0x03, 0x88, 0xda, 0xce, 0x60, 0xb6, 0xa3, 0x92, + 0xf3, 0x28, 0xc2, 0xb9, 0x71, 0xb2, 0xfe, 0x78, + 0xab, 0x6e, 0x47, 0xd4, 0x2c, 0xec, 0x13, 0xbd, + 0xf5, 0x3a, 0x67, 0xb2, 0x12, 0x57, 0xbd, 0xdf + }; + const psa_algorithm_t alg = + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tagLen); + uint8_t out[sizeof(expected)]; + uint8_t dec[sizeof(plaintext)]; + size_t outLen = 0; + size_t decLen = 0; + psa_status_t st; + + st = psa_aead_encrypt(key, alg, nonce, sizeof(nonce), NULL, 0, + plaintext, sizeof(plaintext), out, sizeof(out), + &outLen); + if (tagLen < WOLFSSL_MIN_AUTH_TAG_SZ) { + expect(st == PSA_ERROR_NOT_SUPPORTED, "encrypt NOT_SUPPORTED", + tagLen); + st = psa_aead_decrypt(key, alg, nonce, sizeof(nonce), NULL, 0, + expected, sizeof(plaintext) + tagLen, dec, + sizeof(dec), &decLen); + expect(st == PSA_ERROR_NOT_SUPPORTED, "decrypt NOT_SUPPORTED", + tagLen); + return; + } + + expect(st == PSA_SUCCESS, "encrypt", tagLen); + expect(outLen == sizeof(plaintext) + tagLen, "encrypt length", tagLen); + expect(memcmp(out, expected, sizeof(plaintext) + tagLen) == 0, + "encrypt output", tagLen); + + st = psa_aead_decrypt(key, alg, nonce, sizeof(nonce), NULL, 0, + expected, sizeof(plaintext) + tagLen, dec, + sizeof(dec), &decLen); + expect(st == PSA_SUCCESS, "decrypt", tagLen); + expect(decLen == sizeof(plaintext) && + memcmp(dec, plaintext, sizeof(plaintext)) == 0, + "decrypt output", tagLen); + + memcpy(out, expected, sizeof(plaintext) + tagLen); + out[sizeof(plaintext) + tagLen - 1] ^= 0x01; + st = psa_aead_decrypt(key, alg, nonce, sizeof(nonce), NULL, 0, out, + sizeof(plaintext) + tagLen, dec, sizeof(dec), + &decLen); + expect(st == PSA_ERROR_INVALID_SIGNATURE, "decrypt with a bad tag", + tagLen); +} + +int main(void) +{ + static const uint8_t keyBytes[16] = { 0 }; + static const size_t tagLengths[] = { 4, 8, 12, 16 }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + psa_status_t st; + size_t i; + + st = psa_crypto_init(); + if (st != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init: %d\n", (int)st); + return 1; + } + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, + PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_GCM, 4)); + st = psa_import_key(&attrs, keyBytes, sizeof(keyBytes), &key); + if (st != PSA_SUCCESS) { + printf("FAIL: psa_import_key: %d\n", (int)st); + return 1; + } + + for (i = 0; i < sizeof(tagLengths) / sizeof(tagLengths[0]); i++) { + test_tag_length(key, tagLengths[i]); + } + + psa_destroy_key(key); + + if (failures != 0) { + printf("GCM short tag tests: %d failure(s), minimum %d\n", failures, + WOLFSSL_MIN_AUTH_TAG_SZ); + return 1; + } + printf("GCM short tag tests: all passed, minimum %d\n", + WOLFSSL_MIN_AUTH_TAG_SZ); + return 0; +} diff --git a/test/psa_server/psa_api_test.c b/test/psa_server/psa_api_test.c index c0b6789..71ec47f 100644 --- a/test/psa_server/psa_api_test.c +++ b/test/psa_server/psa_api_test.c @@ -566,6 +566,66 @@ static int test_cmac(void) return TEST_OK; } +#ifdef WOLFSSL_SHA3 +/* HMAC with SHA3, key 32 bytes of 0xaa, message "abc" */ +static int test_hmac_sha3(void) +{ + static const uint8_t exp_256[] = { + 0xa9,0x86,0x41,0x9a,0x16,0x2b,0x6d,0x47, + 0x31,0xa8,0xe9,0x6e,0x44,0xa2,0xc6,0xe7, + 0x84,0xd5,0x01,0x37,0x90,0x7b,0x45,0x7c, + 0x9f,0xb7,0x7c,0x62,0x70,0x5d,0xc4,0xd9, + }; + static const uint8_t exp_512[] = { + 0x2c,0xef,0x45,0xb6,0x95,0x0e,0x41,0xa7, + 0x0b,0xc8,0x5c,0xb4,0x31,0xb2,0x16,0x1d, + 0x47,0xc9,0xe2,0x93,0x21,0x87,0xfa,0x15, + 0xd8,0x0e,0x3b,0x7a,0xf1,0xda,0x38,0xaa, + 0x8f,0xe8,0x23,0xa7,0x2e,0xfd,0x7e,0x53, + 0x6e,0x52,0x36,0xa5,0xb0,0x79,0x84,0x18, + 0xd8,0xc8,0xf0,0x8b,0x0d,0xe5,0xfc,0x26, + 0x28,0x67,0xbb,0x37,0x52,0xb6,0x48,0x2d, + }; + static const struct { + psa_algorithm_t alg; + const uint8_t *mac; + size_t mac_len; + } cases[] = { + { PSA_ALG_HMAC(PSA_ALG_SHA3_256), exp_256, sizeof(exp_256) }, + { PSA_ALG_HMAC(PSA_ALG_SHA3_512), exp_512, sizeof(exp_512) }, + }; + uint8_t key[32]; + uint8_t out[PSA_MAC_MAX_SIZE]; + size_t out_len = 0; + size_t i; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_status_t st; + int ret = TEST_OK; + + memset(key, 0xaa, sizeof(key)); + psa_set_key_type(&attrs, PSA_KEY_TYPE_HMAC); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + for (i = 0; i < sizeof(cases) / sizeof(cases[0]) && ret == TEST_OK; i++) { + psa_set_key_algorithm(&attrs, cases[i].alg); + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(HMAC SHA3)") != TEST_OK) + return TEST_FAIL; + st = psa_mac_compute(key_id, cases[i].alg, (const uint8_t *)"abc", 3, + out, sizeof(out), &out_len); + if (check_status(st, "psa_mac_compute(HMAC SHA3)") != TEST_OK || + check_true(out_len == cases[i].mac_len, + "HMAC SHA3 length") != TEST_OK || + check_buf_eq("HMAC SHA3", out, cases[i].mac, + cases[i].mac_len) != TEST_OK) { + ret = TEST_FAIL; + } + (void)psa_destroy_key(key_id); + } + return ret; +} +#endif /* WOLFSSL_SHA3 */ + static int test_mac_verify_finish_accepts_longer_at_least_length_mac(void) { static const uint8_t key[] = { @@ -805,12 +865,18 @@ static int test_algorithm_none_rejects_key_usage(void) return ret; } +/* alg2 needs WOLFPSA_ENROLLMENT_ALG and a volatile key */ static int test_secondary_algorithm_is_not_supported(void) { static const uint8_t aes_key[16] = { 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c }; +#ifdef WOLFPSA_ENROLLMENT_ALG + const psa_status_t volatile_st = PSA_SUCCESS; +#else + const psa_status_t volatile_st = PSA_ERROR_NOT_SUPPORTED; +#endif /* WOLFPSA_ENROLLMENT_ALG */ psa_key_attributes_t attrs = psa_key_attributes_init(); psa_key_id_t key_id = PSA_KEY_ID_NULL; psa_status_t st; @@ -819,32 +885,34 @@ static int test_secondary_algorithm_is_not_supported(void) psa_set_key_bits(&attrs, 128); psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); psa_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); - attrs.policy.alg2 = PSA_ALG_CTR; + psa_set_key_enrollment_algorithm(&attrs, PSA_ALG_CTR); + + st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); + if (check_true(st == volatile_st, "psa_import_key volatile alg2") != + TEST_OK) { + return TEST_FAIL; + } + (void)psa_destroy_key(key_id); + key_id = PSA_KEY_ID_NULL; + psa_set_key_id(&attrs, PSA_KEY_ID_USER_MIN + 2407u); st = psa_import_key(&attrs, aes_key, sizeof(aes_key), &key_id); if (check_true(st == PSA_ERROR_NOT_SUPPORTED, - "psa_import_key rejects unsupported secondary algorithm policy") != TEST_OK) { + "psa_import_key rejects persistent alg2") != TEST_OK) { return TEST_FAIL; } if (check_true(key_id == PSA_KEY_ID_NULL, - "psa_import_key leaves key id null when alg2 is unsupported") != TEST_OK) { + "import leaves id null (persistent alg2)") != TEST_OK) { return TEST_FAIL; } - psa_reset_key_attributes(&attrs); - psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attrs, 128); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); - psa_set_key_algorithm(&attrs, PSA_ALG_CBC_NO_PADDING); - attrs.policy.alg2 = PSA_ALG_CTR; - st = psa_generate_key(&attrs, &key_id); if (check_true(st == PSA_ERROR_NOT_SUPPORTED, - "psa_generate_key rejects unsupported secondary algorithm policy") != TEST_OK) { + "psa_generate_key rejects persistent alg2") != TEST_OK) { return TEST_FAIL; } if (check_true(key_id == PSA_KEY_ID_NULL, - "psa_generate_key leaves key id null when alg2 is unsupported") != TEST_OK) { + "generate leaves id null (persistent alg2)") != TEST_OK) { return TEST_FAIL; } @@ -1033,11 +1101,6 @@ static int test_cipher_generate_iv(void) if (check_status(st, "psa_cipher_generate_iv") != TEST_OK) goto cleanup; if (check_true(iv1_len == 16, "psa_cipher_generate_iv length") != TEST_OK) goto cleanup; - /* (c) a second generate on the same operation must be rejected. */ - st = psa_cipher_generate_iv(&op, iv2, sizeof(iv2), &iv2_len); - if (check_true(st == PSA_ERROR_BAD_STATE, - "psa_cipher_generate_iv rejects re-generation") != TEST_OK) goto cleanup; - st = psa_cipher_update(&op, plaintext, sizeof(plaintext), enc, sizeof(enc), &enc_len); if (check_status(st, "psa_cipher_update(gen_iv)") != TEST_OK) goto cleanup; st = psa_cipher_finish(&op, enc + enc_len, sizeof(enc) - enc_len, &enc_finish); @@ -1066,6 +1129,12 @@ static int test_cipher_generate_iv(void) if (check_status(st, "psa_cipher_generate_iv(2)") != TEST_OK) goto cleanup; if (check_true(memcmp(iv1, iv2, sizeof(iv1)) != 0, "psa_cipher_generate_iv produces fresh randomness") != TEST_OK) goto cleanup; + + /* (c) a second generate on the same operation must be rejected. */ + st = psa_cipher_generate_iv(&op, iv2, sizeof(iv2), &iv2_len); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_cipher_generate_iv rejects re-generation") != TEST_OK) + goto cleanup; (void)psa_cipher_abort(&op); /* (b) decrypt-direction operations must reject IV generation. */ @@ -1095,6 +1164,67 @@ static int test_cipher_generate_iv(void) return ret; } +/* ECB takes no IV; the failed call aborts, so setup works again */ +static int test_cipher_ecb_iv(void) +{ + static const uint8_t key[16] = { + 0x2b,0x7e,0x15,0x16,0x28,0xae,0xd2,0xa6, + 0xab,0xf7,0x15,0x88,0x09,0xcf,0x4f,0x3c + }; + uint8_t iv[16] = {0}; + size_t iv_len = 0; + psa_key_id_t key_id = 0; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_cipher_operation_t op = psa_cipher_operation_init(); + psa_status_t st; + int ret = TEST_FAIL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attrs, 128); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_ECB_NO_PADDING); + + st = psa_import_key(&attrs, key, sizeof(key), &key_id); + if (check_status(st, "psa_import_key(ecb_iv)") != TEST_OK) goto cleanup; + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_ECB_NO_PADDING); + if (check_status(st, "psa_cipher_encrypt_setup(ecb_iv)") != TEST_OK) + goto cleanup; + st = psa_cipher_set_iv(&op, iv, sizeof(iv)); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_cipher_set_iv rejects ECB") != TEST_OK) goto cleanup; + + st = psa_cipher_decrypt_setup(&op, key_id, PSA_ALG_ECB_NO_PADDING); + if (check_status(st, "psa_cipher_decrypt_setup(ecb_iv)") != TEST_OK) + goto cleanup; + st = psa_cipher_generate_iv(&op, iv, sizeof(iv), &iv_len); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_cipher_generate_iv rejects decrypt") != TEST_OK) + goto cleanup; + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_ECB_NO_PADDING); + if (check_status(st, "psa_cipher_encrypt_setup(ecb_iv 2)") != TEST_OK) + goto cleanup; + st = psa_cipher_generate_iv(&op, iv, sizeof(iv), &iv_len); + if (check_true(st == PSA_ERROR_BAD_STATE, + "psa_cipher_generate_iv rejects ECB") != TEST_OK) + goto cleanup; + + st = psa_cipher_encrypt_setup(&op, key_id, PSA_ALG_ECB_NO_PADDING); + if (check_status(st, "psa_cipher_encrypt_setup(ecb_iv 3)") != TEST_OK) + goto cleanup; + + ret = TEST_OK; + +cleanup: + (void)psa_cipher_abort(&op); + if (key_id != 0) { + (void)psa_destroy_key(key_id); + } + return ret; +} + static int test_cipher_rejects_algorithm_mismatch(void) { static const uint8_t key[16] = { @@ -3141,7 +3271,7 @@ static int test_aead_generate_nonce(void) key_id = 0; #ifdef HAVE_AESCCM - /* (4) CCM requires psa_aead_set_lengths before a nonce can be generated. */ + /* (4) CCM generates a nonce before psa_aead_set_lengths. */ { psa_key_attributes_t ccm_attrs = psa_key_attributes_init(); psa_key_id_t ccm_key_id = 0; @@ -3161,8 +3291,8 @@ static int test_aead_generate_nonce(void) return TEST_FAIL; } st = psa_aead_generate_nonce(&op, nonce, sizeof(nonce), &nonce_len); - if (check_true(st == PSA_ERROR_BAD_STATE, - "generate_nonce CCM without set_lengths rejected") != TEST_OK) { + if (check_status(st, "generate_nonce CCM before set_lengths") + != TEST_OK) { (void)psa_aead_abort(&op); (void)psa_destroy_key(ccm_key_id); return TEST_FAIL; @@ -3449,7 +3579,7 @@ static int test_aead_multipart_length_overflow_rejected(void) psa_key_attributes_t attrs = psa_key_attributes_init(); psa_aead_operation_t op = psa_aead_operation_init(); wolfpsa_aead_ctx_t *ctx; - int ret = TEST_OK; + int ret = TEST_FAIL; psa_status_t st; psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); @@ -3462,7 +3592,7 @@ static int test_aead_multipart_length_overflow_rejected(void) st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); if (check_status(st, "psa_aead_encrypt_setup") != TEST_OK) goto cleanup; - st = psa_aead_set_lengths(&op, SIZE_MAX, SIZE_MAX); + st = psa_aead_set_lengths(&op, 0, 0); if (check_status(st, "psa_aead_set_lengths") != TEST_OK) goto cleanup; st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); if (check_status(st, "psa_aead_set_nonce") != TEST_OK) goto cleanup; @@ -3478,7 +3608,7 @@ static int test_aead_multipart_length_overflow_rejected(void) st = psa_aead_encrypt_setup(&op, key_id, PSA_ALG_GCM); if (check_status(st, "psa_aead_encrypt_setup(plaintext)") != TEST_OK) goto cleanup; - st = psa_aead_set_lengths(&op, 0, SIZE_MAX); + st = psa_aead_set_lengths(&op, 0, 0); if (check_status(st, "psa_aead_set_lengths(plaintext)") != TEST_OK) goto cleanup; st = psa_aead_set_nonce(&op, nonce, sizeof(nonce)); if (check_status(st, "psa_aead_set_nonce(plaintext)") != TEST_OK) goto cleanup; @@ -3491,6 +3621,7 @@ static int test_aead_multipart_length_overflow_rejected(void) ret = TEST_FAIL; goto cleanup; } + ret = TEST_OK; cleanup: psa_aead_abort(&op); @@ -3866,99 +3997,6 @@ static int test_aead_gcm_rejects_short_tags(void) return ret; } -/* A shortened GCM tag round trips, or is NOT_SUPPORTED when below - * WOLFSSL_MIN_AUTH_TAG_SZ. */ -static int test_aead_gcm_shortened_tags(void) -{ - static const uint8_t key[16] = { 0 }; - static const uint8_t nonce[12] = { 0 }; - static const uint8_t plaintext[16] = { 0 }; - /* Ciphertext and full tag, as in test_aead_gcm. */ - static const uint8_t expected[sizeof(plaintext) + 16] = { - 0x03,0x88,0xda,0xce,0x60,0xb6,0xa3,0x92, - 0xf3,0x28,0xc2,0xb9,0x71,0xb2,0xfe,0x78, - 0xab,0x6e,0x47,0xd4,0x2c,0xec,0x13,0xbd, - 0xf5,0x3a,0x67,0xb2,0x12,0x57,0xbd,0xdf - }; - static const size_t tag_lengths[] = { 4, 8, 12 }; - uint8_t out[sizeof(expected)]; - uint8_t dec[sizeof(plaintext)]; - size_t out_len; - size_t dec_len; - size_t tag_len; - size_t i; - psa_algorithm_t alg; - psa_key_id_t key_id = 0; - psa_key_attributes_t attrs = psa_key_attributes_init(); - psa_status_t st; - int ret = TEST_OK; - - psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attrs, 128); - psa_set_key_usage_flags(&attrs, - PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); - psa_set_key_algorithm(&attrs, - PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG( - PSA_ALG_GCM, 4)); - - st = psa_import_key(&attrs, key, sizeof(key), &key_id); - if (check_status(st, "psa_import_key(GCM shortened tags)") != TEST_OK) { - return TEST_FAIL; - } - - for (i = 0; i < sizeof(tag_lengths) / sizeof(tag_lengths[0]) && - ret == TEST_OK; i++) { - tag_len = tag_lengths[i]; - alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, tag_len); - out_len = 0; - dec_len = 0; - - st = psa_aead_encrypt(key_id, alg, nonce, sizeof(nonce), NULL, 0, - plaintext, sizeof(plaintext), - out, sizeof(out), &out_len); - if (tag_len < WOLFSSL_MIN_AUTH_TAG_SZ) { - ret = check_true(st == PSA_ERROR_NOT_SUPPORTED, - "GCM shortened tag encrypt NOT_SUPPORTED"); - if (ret == TEST_OK) { - st = psa_aead_decrypt(key_id, alg, nonce, sizeof(nonce), - NULL, 0, expected, - sizeof(plaintext) + tag_len, - dec, sizeof(dec), &dec_len); - ret = check_true(st == PSA_ERROR_NOT_SUPPORTED, - "GCM shortened tag decrypt NOT_SUPPORTED"); - } - continue; - } - if (check_status(st, "psa_aead_encrypt(GCM shortened tag)") != - TEST_OK || - check_true(out_len == sizeof(plaintext) + tag_len, - "GCM shortened tag length") != TEST_OK || - check_buf_eq("GCM shortened tag", out, expected, out_len) != - TEST_OK) { - ret = TEST_FAIL; - continue; - } - - st = psa_aead_decrypt(key_id, alg, nonce, sizeof(nonce), NULL, 0, - out, out_len, dec, sizeof(dec), &dec_len); - if (check_status(st, "psa_aead_decrypt(GCM shortened tag)") != - TEST_OK || - check_true(dec_len == sizeof(plaintext), - "GCM shortened tag decrypt length") != TEST_OK || - check_buf_eq("GCM shortened tag decrypt", dec, plaintext, - sizeof(plaintext)) != TEST_OK) { - ret = TEST_FAIL; - } - } - - st = psa_destroy_key(key_id); - if (check_status(st, "psa_destroy_key(GCM shortened tags)") != TEST_OK) { - return TEST_FAIL; - } - - return ret; -} - /* F-4096: PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG is a wildcard policy * algorithm and must be rejected by operation setup with * PSA_ERROR_INVALID_ARGUMENT, not accepted as a concrete tag length. */ @@ -7298,7 +7336,7 @@ static int test_kdf_input_key_policy(void) op = psa_key_derivation_operation_init(); psa_reset_key_attributes(&attrs); - psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_type(&attrs, PSA_KEY_TYPE_RAW_DATA); psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); psa_set_key_algorithm(&attrs, PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256)); st = psa_import_key(&attrs, secret, sizeof(secret), &wrong_pbkdf2_key); @@ -7313,7 +7351,7 @@ static int test_kdf_input_key_policy(void) st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_PASSWORD, wrong_pbkdf2_key); if (check_true(st == PSA_ERROR_INVALID_ARGUMENT, - "psa_key_derivation_input_key rejects non-PASSWORD PBKDF2 key type") != TEST_OK) { + "input_key rejects RAW_DATA PBKDF2 password") != TEST_OK) { goto cleanup; } @@ -8908,12 +8946,22 @@ int main(int argc, char** argv) return TEST_FAIL; } } +#ifdef WOLFSSL_SHA3 + if (only == NULL || strcmp(only, "hmac_sha3") == 0) { + if (run_named_test("hmac_sha3", test_hmac_sha3) == TEST_FAIL) + return TEST_FAIL; + } +#endif /* WOLFSSL_SHA3 */ if (only == NULL || strcmp(only, "cipher_cbc") == 0) { if (run_named_test("cipher_cbc", test_cipher_cbc) == TEST_FAIL) return TEST_FAIL; } if (only == NULL || strcmp(only, "cipher_generate_iv") == 0) { if (run_named_test("cipher_generate_iv", test_cipher_generate_iv) == TEST_FAIL) return TEST_FAIL; } + if (only == NULL || strcmp(only, "cipher_ecb_iv") == 0) { + if (run_named_test("cipher_ecb_iv", test_cipher_ecb_iv) == TEST_FAIL) + return TEST_FAIL; + } if (only == NULL || strcmp(only, "cipher_algorithm_mismatch") == 0) { if (run_named_test("cipher_algorithm_mismatch", test_cipher_rejects_algorithm_mismatch) == TEST_FAIL) { @@ -9091,12 +9139,6 @@ int main(int argc, char** argv) return TEST_FAIL; } } - if (only == NULL || strcmp(only, "aead_gcm_shortened_tags") == 0) { - if (run_named_test("aead_gcm_shortened_tags", - test_aead_gcm_shortened_tags) == TEST_FAIL) { - return TEST_FAIL; - } - } if (only == NULL || strcmp(only, "aead_gcm_short_nonce") == 0) { if (run_named_test("aead_gcm_short_nonce", test_aead_gcm_rejects_short_nonce) == TEST_FAIL) { diff --git a/test/psa_server/psa_eddsa_mont_export_test.c b/test/psa_server/psa_eddsa_mont_export_test.c index 2543f66..a5672f7 100644 --- a/test/psa_server/psa_eddsa_mont_export_test.c +++ b/test/psa_server/psa_eddsa_mont_export_test.c @@ -47,6 +47,8 @@ static int test_export_pair_public(psa_ecc_family_t family, psa_key_type_t key_type; uint8_t pub[PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(448)]; size_t pub_size = sizeof(pub); + uint8_t pair[PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(448)]; + size_t pair_len = 0; psa_status_t st; int ok = 0; @@ -76,6 +78,22 @@ static int test_export_pair_public(psa_ecc_family_t family, goto destroy; } + st = psa_export_key(key_id, pair, + PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, bits), + &pair_len); + if (st != PSA_SUCCESS) { + printf("FAIL %s key pair export into PSA_EXPORT_KEY_OUTPUT_SIZE " + "status=%d\n", label, (int)st); + goto destroy; + } + + if (pair_len != PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, bits)) { + printf("FAIL %s key pair export len=%u expected=%u\n", label, + (unsigned)pair_len, + (unsigned)PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, bits)); + goto destroy; + } + ok = 1; destroy: diff --git a/test/psa_server/psa_enrollment_alg_test.c b/test/psa_server/psa_enrollment_alg_test.c new file mode 100644 index 0000000..1b73605 --- /dev/null +++ b/test/psa_server/psa_enrollment_alg_test.c @@ -0,0 +1,361 @@ +/* psa_enrollment_alg_test.c + * + * With WOLFPSA_ENROLLMENT_ALG, a volatile key's enrollment algorithm is a + * second permitted algorithm: each operation accepts either one, and + * psa_copy_key() intersects it. Links a library built with that option. + * + * 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 + */ + +#include +#include +#include + +static int failures; + +static void check(const char *what, const char *step, psa_status_t got, + psa_status_t want) +{ + if (got != want) { + printf("FAIL: %s, %s: got %d want %d\n", what, step, (int)got, + (int)want); + failures++; + } +} + +/* Generate a volatile key with an enrollment algorithm */ +static psa_key_id_t make_key(psa_key_type_t type, size_t bits, + psa_key_usage_t usage, psa_algorithm_t alg, + psa_algorithm_t alg2) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t got = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + psa_status_t status; + + psa_set_key_type(&attrs, type); + psa_set_key_bits(&attrs, bits); + psa_set_key_usage_flags(&attrs, usage); + psa_set_key_algorithm(&attrs, alg); + psa_set_key_enrollment_algorithm(&attrs, alg2); + status = psa_generate_key(&attrs, &key); + check("make key", "generate", status, PSA_SUCCESS); + if (status == PSA_SUCCESS) { + check("make key", "get attributes", psa_get_key_attributes(key, &got), + PSA_SUCCESS); + if (psa_get_key_enrollment_algorithm(&got) != alg2) { + printf("FAIL: enrollment algorithm 0x%08x want 0x%08x\n", + (unsigned)psa_get_key_enrollment_algorithm(&got), + (unsigned)alg2); + failures++; + } + } + return key; +} + +static psa_status_t cipher(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t in[16] = {0}; + uint8_t out[32]; + size_t out_len = 0; + + return psa_cipher_encrypt(key, alg, in, sizeof(in), out, sizeof(out), + &out_len); +} + +static psa_status_t mac(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t out[PSA_MAC_MAX_SIZE]; + size_t out_len = 0; + + return psa_mac_compute(key, alg, (const uint8_t *)"abc", 3, out, + sizeof(out), &out_len); +} + +static psa_status_t aead(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + uint8_t nonce[PSA_AEAD_NONCE_MAX_SIZE] = {0}; + size_t nonce_len = 0; + uint8_t ct[32]; + uint8_t pt[32]; + size_t ct_len = 0; + size_t pt_len = 0; + psa_status_t status; + + status = psa_get_key_attributes(key, &attrs); + if (status == PSA_SUCCESS) { + nonce_len = PSA_AEAD_NONCE_LENGTH(psa_get_key_type(&attrs), alg); + status = psa_aead_encrypt(key, alg, nonce, nonce_len, NULL, 0, + (const uint8_t *)"abc", 3, ct, sizeof(ct), + &ct_len); + } + if (status == PSA_SUCCESS) { + status = psa_aead_decrypt(key, alg, nonce, nonce_len, NULL, 0, ct, + ct_len, pt, sizeof(pt), &pt_len); + } + return status; +} + +static psa_status_t sign(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t hash[32] = {0}; + uint8_t sig[PSA_SIGNATURE_MAX_SIZE]; + size_t sig_len = 0; + + return psa_sign_hash(key, alg, hash, sizeof(hash), sig, sizeof(sig), + &sig_len); +} + +static psa_status_t agree(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t pub[PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(256)]; + uint8_t secret[PSA_BITS_TO_BYTES(256)]; + size_t pub_len = 0; + size_t secret_len = 0; + psa_status_t status; + + status = psa_export_public_key(key, pub, sizeof(pub), &pub_len); + if (status == PSA_SUCCESS) { + status = psa_raw_key_agreement(alg, key, pub, pub_len, secret, + sizeof(secret), &secret_len); + } + return status; +} + +static psa_status_t derive(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_derivation_operation_t op = PSA_KEY_DERIVATION_OPERATION_INIT; + psa_status_t status; + + status = psa_key_derivation_setup(&op, alg); + if (status == PSA_SUCCESS) { + status = psa_key_derivation_input_key( + &op, PSA_KEY_DERIVATION_INPUT_SECRET, key); + } + psa_key_derivation_abort(&op); + return status; +} + +/* Wrap the key with itself, then unwrap it */ +static psa_status_t wrap(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + uint8_t data[32]; + size_t data_len = 0; + psa_key_id_t out = PSA_KEY_ID_NULL; + psa_status_t status; + + status = psa_wrap_key(key, alg, key, data, sizeof(data), &data_len); + if (status == PSA_SUCCESS) { + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CTR); + status = psa_unwrap_key(&attrs, key, alg, data, data_len, &out); + psa_destroy_key(out); + } + return status; +} + +static psa_status_t kem(psa_key_id_t key, psa_algorithm_t alg) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + uint8_t ct[PSA_ENCAPSULATE_CIPHERTEXT_MAX_SIZE]; + size_t ct_len = 0; + psa_key_id_t out = PSA_KEY_ID_NULL; + psa_status_t status; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + status = psa_encapsulate(key, alg, &attrs, &out, ct, sizeof(ct), &ct_len); + psa_destroy_key(out); + out = PSA_KEY_ID_NULL; + if (status == PSA_SUCCESS) { + status = psa_decapsulate(key, alg, ct, ct_len, &attrs, &out); + psa_destroy_key(out); + } + return status; +} + +/* One key per row: alg2 is permitted as well as alg, other (if set) is not */ +static void check_operations(void) +{ + static const struct { + const char *what; + psa_key_type_t type; + size_t bits; + psa_key_usage_t usage; + psa_algorithm_t alg; + psa_algorithm_t alg2; + psa_algorithm_t other; + psa_status_t (*use)(psa_key_id_t key, psa_algorithm_t alg); + } cases[] = { + { "cipher", PSA_KEY_TYPE_AES, 128, PSA_KEY_USAGE_ENCRYPT, + PSA_ALG_CTR, PSA_ALG_CBC_NO_PADDING, PSA_ALG_ECB_NO_PADDING, + cipher }, + { "MAC", PSA_KEY_TYPE_HMAC, 256, PSA_KEY_USAGE_SIGN_MESSAGE, + PSA_ALG_HMAC(PSA_ALG_SHA_256), PSA_ALG_HMAC(PSA_ALG_SHA_384), + PSA_ALG_HMAC(PSA_ALG_SHA_512), mac }, + { "AEAD", PSA_KEY_TYPE_AES, 128, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, + PSA_ALG_GCM, PSA_ALG_CCM, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 12), aead }, + { "XChaCha20-Poly1305", PSA_KEY_TYPE_XCHACHA20, 256, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_NONE, + PSA_ALG_XCHACHA20_POLY1305, PSA_ALG_NONE, aead }, + { "Ascon-AEAD128", PSA_KEY_TYPE_ASCON, 128, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_NONE, + PSA_ALG_ASCON_AEAD128, PSA_ALG_NONE, aead }, + { "key wrap", PSA_KEY_TYPE_AES, 128, + PSA_KEY_USAGE_WRAP | PSA_KEY_USAGE_UNWRAP | PSA_KEY_USAGE_EXPORT, + PSA_ALG_CTR, PSA_ALG_KW, PSA_ALG_NONE, wrap }, + { "ML-KEM", PSA_KEY_TYPE_ML_KEM_KEY_PAIR, 768, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT, PSA_ALG_NONE, + PSA_ALG_ML_KEM, PSA_ALG_NONE, kem }, + { "ECDSA", PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1), 256, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_DERIVE, PSA_ALG_ECDH, + PSA_ALG_ECDSA(PSA_ALG_ANY_HASH), + PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), sign }, + { "ECDH", PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1), 256, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_DERIVE, + PSA_ALG_ECDSA(PSA_ALG_ANY_HASH), PSA_ALG_ECDH, PSA_ALG_NONE, + agree }, + { "KDF", PSA_KEY_TYPE_DERIVE, 256, PSA_KEY_USAGE_DERIVE, + PSA_ALG_HKDF(PSA_ALG_SHA_256), PSA_ALG_HKDF(PSA_ALG_SHA_384), + PSA_ALG_HKDF(PSA_ALG_SHA_512), derive }, + }; + psa_key_id_t key; + psa_algorithm_t use_alg; + size_t i; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + key = make_key(cases[i].type, cases[i].bits, cases[i].usage, + cases[i].alg, cases[i].alg2); + if (key == PSA_KEY_ID_NULL) { + continue; + } + use_alg = cases[i].alg2; + if (use_alg == PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)) { + use_alg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); + } + check(cases[i].what, "enrollment algorithm", + cases[i].use(key, use_alg), PSA_SUCCESS); + if (cases[i].other != PSA_ALG_NONE) { + check(cases[i].what, "other algorithm", + cases[i].use(key, cases[i].other), PSA_ERROR_NOT_PERMITTED); + } + check(cases[i].what, "psa_check_key_usage", + psa_check_key_usage(key, use_alg, 0), PSA_SUCCESS); + psa_destroy_key(key); + } +} + +/* psa_copy_key() intersects alg2, or drops it if either side has none */ +static void check_copy(void) +{ + static const struct { + const char *what; + psa_algorithm_t target_alg2; + psa_status_t want; + psa_algorithm_t want_alg2; + } cases[] = { + { "copy, same alg2", PSA_ALG_CBC_NO_PADDING, PSA_SUCCESS, + PSA_ALG_CBC_NO_PADDING }, + { "copy, no alg2", PSA_ALG_NONE, PSA_SUCCESS, PSA_ALG_NONE }, + { "copy, other alg2", PSA_ALG_ECB_NO_PADDING, + PSA_ERROR_INVALID_ARGUMENT, PSA_ALG_NONE }, + }; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t got = psa_key_attributes_init(); + psa_key_id_t source; + psa_key_id_t copy; + psa_status_t status; + size_t i; + + source = make_key(PSA_KEY_TYPE_AES, 128, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_COPY, + PSA_ALG_CTR, PSA_ALG_CBC_NO_PADDING); + if (source == PSA_KEY_ID_NULL) { + return; + } + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + copy = PSA_KEY_ID_NULL; + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CTR); + psa_set_key_enrollment_algorithm(&attrs, cases[i].target_alg2); + status = psa_copy_key(source, &attrs, ©); + check(cases[i].what, "psa_copy_key", status, cases[i].want); + if (status == PSA_SUCCESS && + (psa_get_key_attributes(copy, &got) != PSA_SUCCESS || + psa_get_key_enrollment_algorithm(&got) != cases[i].want_alg2)) { + printf("FAIL: %s: enrollment algorithm 0x%08x\n", cases[i].what, + (unsigned)psa_get_key_enrollment_algorithm(&got)); + failures++; + } + psa_destroy_key(copy); + } + psa_destroy_key(source); +} + +/* Reading a persistent key clears a stale enrollment algorithm */ +static void check_persistent(void) +{ + static const uint8_t data[16] = {0}; + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t got = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CTR); + psa_set_key_id(&attrs, PSA_KEY_ID_USER_MIN + 2408u); + check("persistent", "import", + psa_import_key(&attrs, data, sizeof(data), &key), PSA_SUCCESS); + + psa_set_key_enrollment_algorithm(&got, PSA_ALG_CBC_NO_PADDING); + check("persistent", "get attributes", psa_get_key_attributes(key, &got), + PSA_SUCCESS); + if (psa_get_key_enrollment_algorithm(&got) != PSA_ALG_NONE) { + printf("FAIL: persistent: enrollment algorithm 0x%08x\n", + (unsigned)psa_get_key_enrollment_algorithm(&got)); + failures++; + } + check("persistent", "enrollment algorithm", + cipher(key, PSA_ALG_CBC_NO_PADDING), PSA_ERROR_NOT_PERMITTED); + psa_destroy_key(key); +} + +int main(void) +{ + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + + check_operations(); + check_copy(); + check_persistent(); + + if (failures == 0) { + printf("Enrollment algorithm tests: all passed\n"); + return 0; + } + printf("Enrollment algorithm tests: %d failure(s)\n", failures); + return 1; +} diff --git a/test/psa_server/psa_import_key_data_test.c b/test/psa_server/psa_import_key_data_test.c new file mode 100644 index 0000000..afd688e --- /dev/null +++ b/test/psa_server/psa_import_key_data_test.c @@ -0,0 +1,200 @@ +/* psa_import_key_data_test.c + * + * psa_import_key() rejects RSA and ECC key data that does not describe a + * valid key of the given type and size. + * + * 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 + */ + +#include +#include +#include + +#define RSA_BITS 1024 + +static int failures; + +static void check_import(psa_key_type_t type, size_t bits, + const uint8_t *data, size_t data_len, + psa_status_t want, size_t want_bits, + const char *what) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_attributes_t got = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + psa_status_t status; + + psa_set_key_type(&attrs, type); + psa_set_key_bits(&attrs, bits); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + status = psa_import_key(&attrs, data, data_len, &key); + if (status != want) { + printf("FAIL: %s: got %d want %d\n", what, (int)status, (int)want); + failures++; + } + else if (status == PSA_SUCCESS && + (psa_get_key_attributes(key, &got) != PSA_SUCCESS || + psa_get_key_bits(&got) != want_bits)) { + printf("FAIL: %s: bits %u want %u\n", what, + (unsigned)psa_get_key_bits(&got), (unsigned)want_bits); + failures++; + } + psa_destroy_key(key); +} + +static psa_status_t generate_export(psa_key_type_t type, size_t bits, + uint8_t *pair, size_t pair_size, + size_t *pair_len, uint8_t *pub, + size_t pub_size, size_t *pub_len) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + psa_status_t status; + + psa_set_key_type(&attrs, type); + psa_set_key_bits(&attrs, bits); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + status = psa_generate_key(&attrs, &key); + if (status == PSA_SUCCESS) { + status = psa_export_key(key, pair, pair_size, pair_len); + } + if (status == PSA_SUCCESS) { + status = psa_export_public_key(key, pub, pub_size, pub_len); + } + psa_destroy_key(key); + return status; +} + +/* RSAPublicKey with a 128-byte modulus whose top byte is 0x3f: 1022 bits */ +static void check_rsa_odd_size(void) +{ + uint8_t der[139]; + + memset(der, 0xff, sizeof(der)); + memcpy(der, "\x30\x81\x88\x02\x81\x80\x3f", 7); + memcpy(der + sizeof(der) - 5, "\x02\x03\x01\x00\x01", 5); + check_import(PSA_KEY_TYPE_RSA_PUBLIC_KEY, 0, der, sizeof(der), + PSA_ERROR_NOT_SUPPORTED, 0, "RSA 1022-bit public key"); +} + +static void check_rsa(void) +{ + static uint8_t pair[PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(RSA_BITS) + 1]; + uint8_t pub[PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(RSA_BITS)]; + size_t pair_len = 0; + size_t pub_len = 0; + psa_status_t status; + + status = generate_export(PSA_KEY_TYPE_RSA_KEY_PAIR, RSA_BITS, pair, + sizeof(pair) - 1, &pair_len, pub, sizeof(pub), + &pub_len); + if (status != PSA_SUCCESS) { + printf("FAIL: generate and export RSA key: %d\n", (int)status); + failures++; + return; + } + + check_import(PSA_KEY_TYPE_RSA_KEY_PAIR, 0, pair, pair_len, PSA_SUCCESS, + RSA_BITS, "RSA key pair"); + check_import(PSA_KEY_TYPE_RSA_KEY_PAIR, RSA_BITS, pair, pair_len, + PSA_SUCCESS, RSA_BITS, "RSA key pair, bits given"); + check_import(PSA_KEY_TYPE_RSA_PUBLIC_KEY, 0, pub, pub_len, PSA_SUCCESS, + RSA_BITS, "RSA public key"); + check_import(PSA_KEY_TYPE_RSA_KEY_PAIR, 2 * RSA_BITS, pair, pair_len, + PSA_ERROR_INVALID_ARGUMENT, 0, "RSA key pair, wrong bits"); + pair[pair_len] = 0; + check_import(PSA_KEY_TYPE_RSA_KEY_PAIR, RSA_BITS, pair, pair_len + 1, + PSA_ERROR_INVALID_ARGUMENT, 0, "RSA key pair, trailing data"); + check_import(PSA_KEY_TYPE_RSA_KEY_PAIR, 0, pair, pair_len - 1, + PSA_ERROR_INVALID_ARGUMENT, 0, "RSA key pair, truncated"); + check_import(PSA_KEY_TYPE_RSA_KEY_PAIR, 0, pub, pub_len, + PSA_ERROR_INVALID_ARGUMENT, 0, "RSA public key as pair"); + check_import(PSA_KEY_TYPE_RSA_PUBLIC_KEY, 0, pair, pair_len, + PSA_ERROR_INVALID_ARGUMENT, 0, "RSA key pair as public"); + check_rsa_odd_size(); +} + +/* The order n of secp256r1 */ +static const uint8_t p256_n[32] = { + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, + 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51 +}; + +static const struct { + uint8_t last; + psa_status_t want; + const char *what; +} p256_scalars[] = { + { 0x50, PSA_SUCCESS, "ECC secp256r1 d = n - 1" }, + { 0x51, PSA_ERROR_INVALID_ARGUMENT, "ECC secp256r1 d = n" }, + { 0x52, PSA_ERROR_INVALID_ARGUMENT, "ECC secp256r1 d = n + 1" }, +}; + +static void check_ecc(void) +{ + const psa_key_type_t type = + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1); + uint8_t d[sizeof(p256_n)]; + uint8_t pair[PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(256)]; + uint8_t pub[PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(256)]; + size_t pair_len = 0; + size_t pub_len = 0; + size_t i; + + memset(d, 0, sizeof(d)); + check_import(type, 0, d, sizeof(d), PSA_ERROR_INVALID_ARGUMENT, 0, + "ECC secp256r1 d = 0"); + for (i = 0; i < sizeof(p256_scalars) / sizeof(p256_scalars[0]); i++) { + memcpy(d, p256_n, sizeof(d)); + d[sizeof(d) - 1] = p256_scalars[i].last; + check_import(type, 0, d, sizeof(d), p256_scalars[i].want, 256, + p256_scalars[i].what); + } + + if (generate_export(type, 256, pair, sizeof(pair), &pair_len, pub, + sizeof(pub), &pub_len) != PSA_SUCCESS) { + printf("FAIL: generate and export ECC key\n"); + failures++; + return; + } + check_import(type, 0, pair, pair_len, PSA_SUCCESS, 256, + "ECC secp256r1 key pair"); + check_import(PSA_KEY_TYPE_RSA_KEY_PAIR, 0, pair, pair_len, + PSA_ERROR_INVALID_ARGUMENT, 0, "ECC key pair as RSA"); +} + +int main(void) +{ + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + + check_rsa(); + check_ecc(); + + if (failures == 0) { + printf("Import key data tests: all passed\n"); + return 0; + } + printf("Import key data tests: %d failure(s)\n", failures); + return 1; +} diff --git a/test/psa_server/psa_kdf_input_key_test.c b/test/psa_server/psa_kdf_input_key_test.c index f070c57..e155c12 100644 --- a/test/psa_server/psa_kdf_input_key_test.c +++ b/test/psa_server/psa_kdf_input_key_test.c @@ -87,21 +87,207 @@ static psa_status_t derive(psa_key_derivation_operation_t *op, return status; } -static psa_status_t import_secret_key(const uint8_t *key, size_t key_len, - psa_algorithm_t kdf_alg, - psa_key_type_t type, - psa_key_id_t *kid) +static psa_status_t import_usage_key(const uint8_t *key, size_t key_len, + psa_algorithm_t kdf_alg, + psa_key_type_t type, + psa_key_usage_t usage, + psa_key_id_t *kid) { psa_key_attributes_t attrs = psa_key_attributes_init(); *kid = PSA_KEY_ID_NULL; psa_set_key_type(&attrs, type); psa_set_key_bits(&attrs, (psa_key_bits_t)(key_len * 8u)); - psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_usage_flags(&attrs, usage); psa_set_key_algorithm(&attrs, kdf_alg); return psa_import_key(&attrs, key, key_len, kid); } +static psa_status_t import_secret_key(const uint8_t *key, size_t key_len, + psa_algorithm_t kdf_alg, + psa_key_type_t type, + psa_key_id_t *kid) +{ + return import_usage_key(key, key_len, kdf_alg, type, + PSA_KEY_USAGE_DERIVE, kid); +} + +static const uint8_t kdf_input[16] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b +}; + +/* Set up alg and, for PBKDF2, input the cost that must come first */ +static psa_status_t kdf_start(psa_key_derivation_operation_t *op, + psa_algorithm_t alg) +{ + psa_status_t status = psa_key_derivation_setup(op, alg); + + if (status == PSA_SUCCESS && PSA_ALG_IS_PBKDF2(alg)) { + status = psa_key_derivation_input_integer( + op, PSA_KEY_DERIVATION_INPUT_COST, 1); + } + return status; +} + +/* Key type accepted or rejected for each input step */ +static void test_input_key_types(void) +{ + static const struct { + const char *name; + psa_algorithm_t alg; + psa_key_derivation_step_t step; + psa_key_type_t type; + psa_status_t expected; + } cases[] = { + { "HKDF secret DERIVE", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SECRET, PSA_KEY_TYPE_DERIVE, + PSA_SUCCESS }, + { "HKDF secret RAW_DATA", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SECRET, PSA_KEY_TYPE_RAW_DATA, + PSA_ERROR_INVALID_ARGUMENT }, + { "HKDF salt RAW_DATA", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SALT, PSA_KEY_TYPE_RAW_DATA, + PSA_SUCCESS }, + { "HKDF salt DERIVE", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SALT, PSA_KEY_TYPE_DERIVE, + PSA_ERROR_INVALID_ARGUMENT }, + { "HKDF info RAW_DATA", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_INFO, PSA_KEY_TYPE_RAW_DATA, + PSA_SUCCESS }, + { "TLS 1.2 PRF seed RAW_DATA", PSA_ALG_TLS12_PRF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SEED, PSA_KEY_TYPE_RAW_DATA, + PSA_SUCCESS }, + { "PBKDF2 password PASSWORD", PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_PASSWORD, PSA_KEY_TYPE_PASSWORD, + PSA_SUCCESS }, + { "PBKDF2 password DERIVE", PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_PASSWORD, PSA_KEY_TYPE_DERIVE, + PSA_SUCCESS }, + { "PBKDF2 password RAW_DATA", PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_PASSWORD, PSA_KEY_TYPE_RAW_DATA, + PSA_ERROR_INVALID_ARGUMENT }, + { "PBKDF2 salt RAW_DATA", PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SALT, PSA_KEY_TYPE_RAW_DATA, + PSA_SUCCESS }, + { "SP800-108 context RAW_DATA", + PSA_ALG_SP800_108_COUNTER_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_CONTEXT, PSA_KEY_TYPE_RAW_DATA, + PSA_SUCCESS }, + }; + psa_key_derivation_operation_t op; + psa_key_id_t kid; + psa_status_t status; + size_t i; + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + op = psa_key_derivation_operation_init(); + status = kdf_start(&op, cases[i].alg); + if (status == PSA_ERROR_NOT_SUPPORTED) { + continue; + } + if (expect_status(cases[i].name, status, PSA_SUCCESS) != 0 || + expect_status(cases[i].name, + import_secret_key(kdf_input, sizeof(kdf_input), + cases[i].alg, cases[i].type, + &kid), PSA_SUCCESS) != 0) { + failures++; + psa_key_derivation_abort(&op); + continue; + } + status = psa_key_derivation_input_key(&op, cases[i].step, kid); + if (expect_status(cases[i].name, status, cases[i].expected) != 0) + failures++; + psa_key_derivation_abort(&op); + psa_destroy_key(kid); + } +} + +/* output_key needs the secret or password to come from a DERIVE key. + * A usage of 0 inputs the secret or password as bytes. */ +static void test_output_key_permission(void) +{ + static const struct { + const char *name; + psa_algorithm_t alg; + psa_key_derivation_step_t step; + psa_key_usage_t usage; + psa_status_t expected; + } cases[] = { + { "HKDF secret from key", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SECRET, PSA_KEY_USAGE_DERIVE, + PSA_SUCCESS }, + { "HKDF secret from verify-only key", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SECRET, PSA_KEY_USAGE_VERIFY_DERIVATION, + PSA_ERROR_NOT_PERMITTED }, + { "HKDF secret from bytes", PSA_ALG_HKDF(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_SECRET, 0, PSA_ERROR_NOT_PERMITTED }, + { "PBKDF2 password from key", PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_PASSWORD, PSA_KEY_USAGE_DERIVE, + PSA_SUCCESS }, + { "PBKDF2 password from verify-only key", + PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_PASSWORD, PSA_KEY_USAGE_VERIFY_DERIVATION, + PSA_ERROR_NOT_PERMITTED }, + { "PBKDF2 password from bytes", + PSA_ALG_PBKDF2_HMAC(PSA_ALG_SHA_256), + PSA_KEY_DERIVATION_INPUT_PASSWORD, 0, PSA_ERROR_NOT_PERMITTED }, + }; + psa_key_attributes_t out_attrs = psa_key_attributes_init(); + psa_key_derivation_operation_t op; + psa_key_id_t kid = PSA_KEY_ID_NULL; + psa_key_id_t out_id = PSA_KEY_ID_NULL; + psa_status_t status; + size_t i; + + psa_set_key_type(&out_attrs, PSA_KEY_TYPE_RAW_DATA); + psa_set_key_bits(&out_attrs, 128); + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + op = psa_key_derivation_operation_init(); + status = kdf_start(&op, cases[i].alg); + if (status == PSA_ERROR_NOT_SUPPORTED) { + continue; + } + if (status == PSA_SUCCESS && PSA_ALG_IS_PBKDF2(cases[i].alg)) { + status = psa_key_derivation_input_bytes( + &op, PSA_KEY_DERIVATION_INPUT_SALT, kdf_input, + sizeof(kdf_input)); + } + if (status == PSA_SUCCESS && cases[i].usage != 0) { + status = import_usage_key(kdf_input, sizeof(kdf_input), + cases[i].alg, PSA_KEY_TYPE_DERIVE, + cases[i].usage, &kid); + if (status == PSA_SUCCESS) + status = psa_key_derivation_input_key(&op, cases[i].step, + kid); + } + else if (status == PSA_SUCCESS) { + status = psa_key_derivation_input_bytes(&op, cases[i].step, + kdf_input, + sizeof(kdf_input)); + } + if (status == PSA_SUCCESS && PSA_ALG_IS_HKDF(cases[i].alg)) { + status = psa_key_derivation_input_bytes( + &op, PSA_KEY_DERIVATION_INPUT_INFO, kdf_input, + sizeof(kdf_input)); + } + if (expect_status(cases[i].name, status, PSA_SUCCESS) != 0) { + failures++; + } + else { + status = psa_key_derivation_output_key(&out_attrs, &op, &out_id); + if (expect_status(cases[i].name, status, cases[i].expected) != 0) + failures++; + } + psa_key_derivation_abort(&op); + psa_destroy_key(out_id); + psa_destroy_key(kid); + out_id = PSA_KEY_ID_NULL; + kid = PSA_KEY_ID_NULL; + } +} + int main(void) { static const uint8_t aes_key[16] = { @@ -217,6 +403,9 @@ int main(void) psa_destroy_key(aes_id); psa_destroy_key(hmac_id); + test_input_key_types(); + test_output_key_permission(); + if (failures == 0) { printf("input-key tests: all passed\n"); return 0; diff --git a/test/psa_server/psa_pure_eddsa_context_test.c b/test/psa_server/psa_pure_eddsa_context_test.c index da2cc45..665515e 100644 --- a/test/psa_server/psa_pure_eddsa_context_test.c +++ b/test/psa_server/psa_pure_eddsa_context_test.c @@ -135,6 +135,48 @@ static int test_pure_eddsa_rejects_context(void) return rc; } +static int test_pure_eddsa_sign_output_size(psa_key_bits_t bits) +{ + const psa_key_type_t type = + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS); + const size_t sig_size = + PSA_SIGN_OUTPUT_SIZE(type, bits, PSA_ALG_PURE_EDDSA); + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key_id = PSA_KEY_ID_NULL; + psa_status_t st; + uint8_t message[32]; + uint8_t signature[128]; + size_t signature_length = 0; + + memset(message, 0xab, sizeof(message)); + psa_set_key_type(&attrs, type); + psa_set_key_bits(&attrs, bits); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_SIGN_MESSAGE); + psa_set_key_algorithm(&attrs, PSA_ALG_PURE_EDDSA); + + st = psa_generate_key(&attrs, &key_id); + if (st == PSA_SUCCESS) { + st = psa_sign_message(key_id, PSA_ALG_PURE_EDDSA, + message, sizeof(message), signature, + sig_size, &signature_length); + psa_destroy_key(key_id); + } + if (st != PSA_SUCCESS) { + printf("FAIL pure_eddsa %u-bit sign into PSA_SIGN_OUTPUT_SIZE: %d\n", + (unsigned)bits, (int)st); + return 1; + } + if (signature_length != sig_size) { + printf("FAIL pure_eddsa %u-bit signature len=%u expected=%u\n", + (unsigned)bits, (unsigned)signature_length, + (unsigned)sig_size); + return 1; + } + printf("PASS pure_eddsa %u-bit sign into PSA_SIGN_OUTPUT_SIZE\n", + (unsigned)bits); + return 0; +} + int main(void) { int rc; @@ -144,6 +186,8 @@ int main(void) return 1; } rc = test_pure_eddsa_rejects_context(); + rc |= test_pure_eddsa_sign_output_size(255); + rc |= test_pure_eddsa_sign_output_size(448); if (rc == 0) { printf("psa_pure_eddsa_context_test: all tests passed\n"); } diff --git a/test/psa_server/psa_reset_outputs_test.c b/test/psa_server/psa_reset_outputs_test.c new file mode 100644 index 0000000..31a9ddc --- /dev/null +++ b/test/psa_server/psa_reset_outputs_test.c @@ -0,0 +1,206 @@ +/* psa_reset_outputs_test.c + * + * A failed call clears its outputs: output lengths are 0, key ids are + * PSA_KEY_ID_NULL and psa_get_key_attributes() leaves initialized + * attributes. + * + * 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 + */ + +#include +#include +#include +#include + +#define MISSING_KEY PSA_KEY_ID_USER_MAX +#define STALE_KEY ((psa_key_id_t)0x5a5a) + +static int failures; + +static void check_length(psa_status_t status, const size_t *length, + const char *what) +{ + if (status == PSA_SUCCESS || *length != 0) { + printf("FAIL: %s: status %d, length %zu\n", what, (int)status, + *length); + failures++; + } +} + +static void check_key(psa_status_t status, const psa_key_id_t *key, + const char *what) +{ + if (status == PSA_SUCCESS || *key != PSA_KEY_ID_NULL) { + printf("FAIL: %s: status %d, key 0x%x\n", what, (int)status, + (unsigned)*key); + failures++; + } +} + +/* Preset the output to a stale value, then run a call that fails */ +#define CHECK_LENGTH(length, call) \ + do { \ + (length) = SIZE_MAX; \ + check_length((call), &(length), #call); \ + } while (0) + +#define CHECK_KEY(key, call) \ + do { \ + (key) = STALE_KEY; \ + check_key((call), &(key), #call); \ + } while (0) + +static void check_keys(void) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_derivation_operation_t kdf = PSA_KEY_DERIVATION_OPERATION_INIT; + uint8_t buf[32]; + psa_key_id_t key; + size_t len; + psa_status_t status; + + memset(buf, 0, sizeof(buf)); + psa_set_key_type(&attrs, PSA_KEY_TYPE_NONE); + psa_set_key_bits(&attrs, 128); + CHECK_KEY(key, psa_generate_key(&attrs, &key)); + CHECK_KEY(key, psa_generate_key_custom(&attrs, NULL, NULL, 0, &key)); + CHECK_KEY(key, psa_key_derivation_output_key(&attrs, &kdf, &key)); + CHECK_KEY(key, psa_key_derivation_output_key_custom(&attrs, &kdf, NULL, + NULL, 0, &key)); + + /* NULL arguments */ + CHECK_KEY(key, psa_import_key(&attrs, NULL, 0, &key)); + CHECK_KEY(key, psa_copy_key(MISSING_KEY, NULL, &key)); + CHECK_KEY(key, psa_key_agreement(MISSING_KEY, buf, sizeof(buf), + PSA_ALG_ECDH, NULL, &key)); + CHECK_KEY(key, psa_encapsulate(MISSING_KEY, PSA_ALG_ML_KEM, NULL, &key, + buf, sizeof(buf), &len)); + CHECK_LENGTH(len, psa_encapsulate(MISSING_KEY, PSA_ALG_ML_KEM, NULL, &key, + buf, sizeof(buf), &len)); + CHECK_KEY(key, psa_decapsulate(MISSING_KEY, PSA_ALG_ML_KEM, NULL, 0, + &attrs, &key)); + CHECK_KEY(key, psa_unwrap_key(&attrs, MISSING_KEY, PSA_ALG_KW, NULL, 0, + &key)); + + psa_set_key_type(&attrs, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attrs, PSA_ALG_CTR); + status = psa_get_key_attributes(MISSING_KEY, &attrs); + if (status == PSA_SUCCESS || psa_get_key_type(&attrs) != 0 || + psa_get_key_bits(&attrs) != 0 || + psa_get_key_usage_flags(&attrs) != 0 || + psa_get_key_algorithm(&attrs) != 0 || + psa_get_key_id(&attrs) != PSA_KEY_ID_NULL) { + printf("FAIL: psa_get_key_attributes: status %d, attributes kept\n", + (int)status); + failures++; + } +} + +static void check_lengths(void) +{ + const psa_algorithm_t ecdsa = PSA_ALG_ECDSA(PSA_ALG_SHA_256); + const psa_algorithm_t rsa = PSA_ALG_RSA_PKCS1V15_CRYPT; + const psa_algorithm_t hmac = PSA_ALG_HMAC(PSA_ALG_SHA_256); + uint8_t in[32]; + uint8_t out[64]; + size_t len; + + memset(in, 0, sizeof(in)); + CHECK_LENGTH(len, psa_export_key(MISSING_KEY, out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_export_public_key(MISSING_KEY, out, sizeof(out), + &len)); + CHECK_LENGTH(len, psa_hash_compute(PSA_ALG_NONE, in, sizeof(in), out, + sizeof(out), &len)); + CHECK_LENGTH(len, psa_sign_hash(MISSING_KEY, ecdsa, in, sizeof(in), out, + sizeof(out), &len)); + CHECK_LENGTH(len, psa_sign_message(MISSING_KEY, ecdsa, in, sizeof(in), + out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_asymmetric_encrypt(MISSING_KEY, rsa, in, sizeof(in), + NULL, 0, out, sizeof(out), + &len)); + CHECK_LENGTH(len, psa_asymmetric_decrypt(MISSING_KEY, rsa, in, sizeof(in), + NULL, 0, out, sizeof(out), + &len)); + CHECK_LENGTH(len, psa_raw_key_agreement(PSA_ALG_ECDH, MISSING_KEY, in, + sizeof(in), out, sizeof(out), + &len)); + CHECK_LENGTH(len, psa_wrap_key(MISSING_KEY, PSA_ALG_KW, MISSING_KEY, out, + sizeof(out), &len)); + CHECK_LENGTH(len, psa_mac_compute(MISSING_KEY, hmac, in, sizeof(in), out, + sizeof(out), &len)); + CHECK_LENGTH(len, psa_cipher_encrypt(MISSING_KEY, PSA_ALG_CTR, in, + sizeof(in), out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_cipher_decrypt(MISSING_KEY, PSA_ALG_CTR, in, + sizeof(in), out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_aead_encrypt(MISSING_KEY, PSA_ALG_GCM, in, 12, NULL, + 0, in, sizeof(in), out, sizeof(out), + &len)); + CHECK_LENGTH(len, psa_aead_decrypt(MISSING_KEY, PSA_ALG_GCM, in, 12, NULL, + 0, in, sizeof(in), out, sizeof(out), + &len)); +} + +/* Each operation is inactive, so every call fails */ +static void check_multipart(void) +{ + psa_hash_operation_t hash = PSA_HASH_OPERATION_INIT; + psa_mac_operation_t mac = PSA_MAC_OPERATION_INIT; + psa_cipher_operation_t cipher = PSA_CIPHER_OPERATION_INIT; + psa_aead_operation_t aead = PSA_AEAD_OPERATION_INIT; + uint8_t in[16]; + uint8_t out[64]; + size_t len; + size_t tag_len; + + memset(in, 0, sizeof(in)); + CHECK_LENGTH(len, psa_hash_finish(&hash, out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_mac_sign_finish(&mac, out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_cipher_generate_iv(&cipher, out, sizeof(out), + &len)); + CHECK_LENGTH(len, psa_cipher_finish(&cipher, out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_aead_generate_nonce(&aead, out, sizeof(out), &len)); + CHECK_LENGTH(len, psa_aead_update(&aead, in, sizeof(in), out, sizeof(out), + &len)); + CHECK_LENGTH(len, psa_aead_finish(&aead, out, sizeof(out), &len, in, + sizeof(in), &tag_len)); + CHECK_LENGTH(tag_len, psa_aead_finish(&aead, out, sizeof(out), &len, in, + sizeof(in), &tag_len)); + CHECK_LENGTH(len, psa_aead_verify(&aead, out, sizeof(out), &len, in, + sizeof(in))); +} + +int main(void) +{ + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + + check_keys(); + check_lengths(); + check_multipart(); + + if (failures == 0) { + printf("reset outputs tests: all passed\n"); + return 0; + } + printf("reset outputs tests: %d failure(s)\n", failures); + return 1; +} diff --git a/test/psa_server/psa_rsa_status_test.c b/test/psa_server/psa_rsa_status_test.c new file mode 100644 index 0000000..bd97e3a --- /dev/null +++ b/test/psa_server/psa_rsa_status_test.c @@ -0,0 +1,200 @@ +/* psa_rsa_status_test.c + * + * RSA operations report a bad input with the status PSA defines for the + * operation: INVALID_SIGNATURE for a signature that does not verify, + * INVALID_PADDING for a ciphertext that does not decrypt, INVALID_ARGUMENT + * for a hash or message of the wrong length. + * + * 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 + */ + +#include +#include +#include + +#define KEY_BITS 2048 +#define KEY_BYTES (KEY_BITS / 8) +/* PKCS#1 v1.5 padding needs 11 bytes */ +#define V15_TOO_LONG (KEY_BYTES - 10) + +static int failures; + +static void expect(psa_status_t got, psa_status_t want, const char *what) +{ + if (got != want) { + printf("FAIL: %s: got %d want %d\n", what, (int)got, (int)want); + failures++; + } +} + +static psa_key_id_t import_pair(const uint8_t *der, size_t der_len, + psa_algorithm_t alg) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + + psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_usage_flags(&attrs, + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attrs, alg); + expect(psa_import_key(&attrs, der, der_len, &key), PSA_SUCCESS, + "import key pair"); + return key; +} + +static void check_v15_sign(const uint8_t *der, size_t der_len) +{ + const psa_algorithm_t alg = PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256); + psa_key_id_t key = import_pair(der, der_len, alg); + uint8_t hash[PSA_HASH_LENGTH(PSA_ALG_SHA_256)]; + uint8_t sig[KEY_BYTES + 1]; + size_t sig_len = 0; + psa_status_t status; + + memset(hash, 0x2a, sizeof(hash)); + memset(sig, 0, sizeof(sig)); + status = psa_sign_hash(key, alg, hash, sizeof(hash), sig, sizeof(sig), + &sig_len); + expect(status, PSA_SUCCESS, "v1.5 sign"); + if (status == PSA_SUCCESS) { + expect(psa_verify_hash(key, alg, hash, sizeof(hash), sig, + sig_len - 1), + PSA_ERROR_INVALID_SIGNATURE, "v1.5 verify, signature short"); + expect(psa_verify_hash(key, alg, hash, sizeof(hash), sig, + sig_len + 1), + PSA_ERROR_INVALID_SIGNATURE, "v1.5 verify, signature long"); + } + psa_destroy_key(key); +} + +static void check_v15_raw(const uint8_t *der, size_t der_len) +{ + const psa_algorithm_t alg = PSA_ALG_RSA_PKCS1V15_SIGN_RAW; + psa_key_id_t key = import_pair(der, der_len, alg); + uint8_t msg[V15_TOO_LONG]; + uint8_t sig[KEY_BYTES]; + size_t sig_len = 0; + + memset(msg, 0x5c, sizeof(msg)); + expect(psa_sign_hash(key, alg, msg, sizeof(msg), sig, sizeof(sig), + &sig_len), + PSA_ERROR_INVALID_ARGUMENT, "raw v1.5 sign, input too long"); + psa_destroy_key(key); +} + +static void check_pss(const uint8_t *der, size_t der_len) +{ + const psa_algorithm_t alg = PSA_ALG_RSA_PSS(PSA_ALG_SHA_256); + psa_key_id_t key = import_pair(der, der_len, alg); + uint8_t hash[PSA_HASH_LENGTH(PSA_ALG_SHA_256)]; + uint8_t sig[KEY_BYTES]; + size_t sig_len = 0; + psa_status_t status; + + memset(hash, 0x2a, sizeof(hash)); + memset(sig, 0, sizeof(sig)); + expect(psa_sign_hash(key, alg, hash, sizeof(hash) - 1, sig, sizeof(sig), + &sig_len), + PSA_ERROR_INVALID_ARGUMENT, "PSS sign, hash short"); + expect(psa_verify_hash(key, alg, hash, sizeof(hash) - 1, sig, + sizeof(sig)), + PSA_ERROR_INVALID_ARGUMENT, "PSS verify, hash short"); + + status = psa_sign_hash(key, alg, hash, sizeof(hash), sig, sizeof(sig), + &sig_len); + expect(status, PSA_SUCCESS, "PSS sign"); + if (status == PSA_SUCCESS) { + sig[sig_len - 1] ^= 0x01; + expect(psa_verify_hash(key, alg, hash, sizeof(hash), sig, sig_len), + PSA_ERROR_INVALID_SIGNATURE, "PSS verify, signature changed"); + } + psa_destroy_key(key); +} + +static void check_encrypt(const uint8_t *der, size_t der_len) +{ + const psa_algorithm_t v15 = PSA_ALG_RSA_PKCS1V15_CRYPT; + const psa_algorithm_t oaep = PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256); + static const uint8_t label[] = { 'a', 'b', 'c' }; + psa_key_id_t key = import_pair(der, der_len, v15); + uint8_t msg[V15_TOO_LONG]; + uint8_t out[KEY_BYTES]; + size_t out_len = 0; + uint8_t plain[KEY_BYTES]; + size_t plain_len = 0; + psa_status_t status; + + memset(msg, 0x5c, sizeof(msg)); + expect(psa_asymmetric_encrypt(key, v15, msg, sizeof(msg), NULL, 0, out, + sizeof(out), &out_len), + PSA_ERROR_INVALID_ARGUMENT, "v1.5 encrypt, input too long"); + psa_destroy_key(key); + + key = import_pair(der, der_len, oaep); + status = psa_asymmetric_encrypt(key, oaep, msg, 16, label, sizeof(label), + out, sizeof(out), &out_len); + expect(status, PSA_SUCCESS, "OAEP encrypt"); + if (status == PSA_SUCCESS) { + expect(psa_asymmetric_decrypt(key, oaep, out, out_len, NULL, 0, + plain, sizeof(plain), &plain_len), + PSA_ERROR_INVALID_PADDING, "OAEP decrypt, wrong label"); + } + psa_destroy_key(key); +} + +int main(void) +{ + psa_key_attributes_t attrs = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + static uint8_t der[PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(KEY_BITS)]; + size_t der_len = 0; + psa_status_t status; + + status = psa_crypto_init(); + expect(status, PSA_SUCCESS, "psa_crypto_init"); + if (status != PSA_SUCCESS) { + return 1; + } + + psa_set_key_type(&attrs, PSA_KEY_TYPE_RSA_KEY_PAIR); + psa_set_key_bits(&attrs, KEY_BITS); + psa_set_key_usage_flags(&attrs, PSA_KEY_USAGE_EXPORT); + status = psa_generate_key(&attrs, &key); + if (status == PSA_SUCCESS) { + status = psa_export_key(key, der, sizeof(der), &der_len); + } + psa_destroy_key(key); + expect(status, PSA_SUCCESS, "generate and export key pair"); + if (status != PSA_SUCCESS) { + return 1; + } + + check_v15_sign(der, der_len); + check_v15_raw(der, der_len); + check_pss(der, der_len); + check_encrypt(der, der_len); + + if (failures == 0) { + printf("RSA status tests: all passed\n"); + return 0; + } + printf("RSA status tests: %d failure(s)\n", failures); + return 1; +} diff --git a/test/psa_server/psa_sp800_108_test.c b/test/psa_server/psa_sp800_108_test.c index a989b2d..5cdd6c0 100644 --- a/test/psa_server/psa_sp800_108_test.c +++ b/test/psa_server/psa_sp800_108_test.c @@ -720,21 +720,34 @@ static int test_hmac_output_key(void) psa_algorithm_t alg = PSA_ALG_SP800_108_COUNTER_HMAC(PSA_ALG_SHA_256); psa_key_derivation_operation_t op = psa_key_derivation_operation_init(); psa_key_attributes_t out_attrs = psa_key_attributes_init(); + psa_key_attributes_t key_attrs = psa_key_attributes_init(); + psa_key_id_t kid = PSA_KEY_ID_NULL; psa_key_id_t derived_key = PSA_KEY_ID_NULL; psa_status_t st; + psa_set_key_type(&key_attrs, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&key_attrs, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&key_attrs, alg); + st = psa_import_key(&key_attrs, g_hmac_secret, sizeof(g_hmac_secret), &kid); + if (expect_status("tc6 import DERIVE key", st, PSA_SUCCESS) != 0) return 1; + st = psa_key_derivation_setup(&op, alg); - if (expect_status("tc6 setup", st, PSA_SUCCESS) != 0) return 1; + if (expect_status("tc6 setup", st, PSA_SUCCESS) != 0) { + psa_destroy_key(kid); + return 1; + } st = psa_key_derivation_set_capacity(&op, 16u); if (expect_status("tc6 set_capacity", st, PSA_SUCCESS) != 0) { + psa_destroy_key(kid); psa_key_derivation_abort(&op); return 1; } - st = psa_key_derivation_input_bytes(&op, PSA_KEY_DERIVATION_INPUT_SECRET, - g_hmac_secret, sizeof(g_hmac_secret)); - if (expect_status("tc6 input SECRET", st, PSA_SUCCESS) != 0) { + st = psa_key_derivation_input_key(&op, PSA_KEY_DERIVATION_INPUT_SECRET, + kid); + psa_destroy_key(kid); + if (expect_status("tc6 input_key SECRET", st, PSA_SUCCESS) != 0) { psa_key_derivation_abort(&op); return 1; } diff --git a/test/psa_server/psa_tf_psa_pk_test.c b/test/psa_server/psa_tf_psa_pk_test.c new file mode 100644 index 0000000..7137db2 --- /dev/null +++ b/test/psa_server/psa_tf_psa_pk_test.c @@ -0,0 +1,147 @@ +/* psa_tf_psa_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 + */ + +/* mbedtls/pk.h on wolfSSL's test keys. PK_CERTS_DIR is wolfSSL's certs/. */ + +#include + +#include +#include + +#define CERT(name) PK_CERTS_DIR "/" name +#define RSA_KEY CERT("server-key.pem") +#define EC_KEY CERT("ecc-key.pem") +#define INVALID MBEDTLS_ERR_PK_KEY_INVALID_FORMAT + +static const struct { + const char* file; + int ret; +} parseCases[] = { + { RSA_KEY, 0 }, + { CERT("server-key.der"), 0 }, + { CERT("server-keyPkcs8.pem"), 0 }, + { CERT("server-keyPkcs8.der"), 0 }, + { EC_KEY, 0 }, + { CERT("ecc-key.der"), 0 }, + { CERT("ecc-keyPkcs8.pem"), 0 }, + { CERT("ecc-keyPkcs8.der"), 0 }, + { CERT("ca-ecc384-key.pem"), 0 }, + { CERT("server-keyEnc.pem"), INVALID }, + { CERT("server-keyPkcs8Enc.pem"), INVALID }, + { CERT("server-cert.pem"), INVALID }, + { CERT("missing.key"), MBEDTLS_ERR_PK_FILE_IO_ERROR } +}; + +static const struct { + const char* file; + psa_algorithm_t alg; + psa_key_usage_t usage; + int can; +} canDoCases[] = { + { RSA_KEY, PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256), + PSA_KEY_USAGE_SIGN_HASH, 1 }, + { RSA_KEY, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256), PSA_KEY_USAGE_VERIFY_HASH, 1 }, + { RSA_KEY, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), PSA_KEY_USAGE_DECRYPT, 1 }, + { RSA_KEY, PSA_ALG_RSA_PKCS1V15_CRYPT, PSA_KEY_USAGE_ENCRYPT, 1 }, + { RSA_KEY, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256), PSA_KEY_USAGE_DECRYPT, 0 }, + { RSA_KEY, PSA_ALG_ECDSA(PSA_ALG_SHA_256), PSA_KEY_USAGE_SIGN_HASH, 0 }, + { EC_KEY, PSA_ALG_ECDSA(PSA_ALG_SHA_256), PSA_KEY_USAGE_SIGN_HASH, 1 }, + { EC_KEY, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), + PSA_KEY_USAGE_VERIFY_HASH, 1 }, + { EC_KEY, PSA_ALG_ECDSA(PSA_ALG_SHA_256), PSA_KEY_USAGE_ENCRYPT, 0 }, + { EC_KEY, PSA_ALG_RSA_PSS(PSA_ALG_SHA_256), PSA_KEY_USAGE_SIGN_HASH, 0 } +}; + +static int fails; + +static void Check(int ok, const char* name) +{ + printf("%s: %s\n", ok ? "PASS" : "FAIL", name); + if (!ok) { + fails++; + } +} + +static void Parse(void) +{ + mbedtls_pk_context pk; + size_t i; + + for (i = 0; i < sizeof(parseCases) / sizeof(parseCases[0]); i++) { + mbedtls_pk_init(&pk); + Check(mbedtls_pk_parse_keyfile(&pk, parseCases[i].file, NULL) == + parseCases[i].ret, parseCases[i].file); + mbedtls_pk_free(&pk); + } +} + +static void CanDo(void) +{ + mbedtls_pk_context pk; + char name[32]; + size_t i; + + for (i = 0; i < sizeof(canDoCases) / sizeof(canDoCases[0]); i++) { + (void)snprintf(name, sizeof(name), "can_do_psa case %u", + (unsigned)i); + mbedtls_pk_init(&pk); + Check(mbedtls_pk_parse_keyfile(&pk, canDoCases[i].file, NULL) == 0 && + mbedtls_pk_can_do_psa(&pk, canDoCases[i].alg, + canDoCases[i].usage) == canDoCases[i].can, name); + mbedtls_pk_free(&pk); + } +} + +/* An empty buffer, a second key into one context, NULL and empty contexts */ +static void BadInput(void) +{ + static const unsigned char der[] = { 0x30, 0x00 }; + mbedtls_pk_context pk; + + mbedtls_pk_init(&pk); + Check(mbedtls_pk_can_do_psa(&pk, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + PSA_KEY_USAGE_SIGN_HASH) == 0, "can_do_psa without a key"); + Check(mbedtls_pk_can_do_psa(NULL, PSA_ALG_ECDSA(PSA_ALG_SHA_256), + PSA_KEY_USAGE_SIGN_HASH) == 0, "can_do_psa NULL"); + Check(mbedtls_pk_parse_key(NULL, der, sizeof(der), NULL, 0) == + PSA_ERROR_INVALID_ARGUMENT, "parse_key NULL"); + Check(mbedtls_pk_parse_key(&pk, der, 0, NULL, 0) == INVALID, + "parse_key empty"); + Check(mbedtls_pk_parse_keyfile(&pk, EC_KEY, NULL) == 0 && + mbedtls_pk_parse_keyfile(&pk, RSA_KEY, NULL) == + PSA_ERROR_INVALID_ARGUMENT, "parse_key second key"); + mbedtls_pk_free(&pk); + mbedtls_pk_free(&pk); + mbedtls_pk_free(NULL); +} + +int main(void) +{ + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + Parse(); + CanDo(); + BadInput(); + mbedtls_psa_crypto_free(); + return (fails == 0) ? 0 : 1; +} diff --git a/test/psa_server/psa_tf_psa_threading_test.c b/test/psa_server/psa_tf_psa_threading_test.c new file mode 100644 index 0000000..757d902 --- /dev/null +++ b/test/psa_server/psa_tf_psa_threading_test.c @@ -0,0 +1,228 @@ +/* psa_tf_psa_threading_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 + */ + +/* Mutexes and condition variables through the TF-PSA-Crypto 1.x API, and PSA + * calls from several threads at once, as MBEDTLS_THREADING_C promises. */ + +#include +#include + +#include +#include + +#define THREADS 4 +#define ROUNDS 10000 +#define KEYS 100 + +static mbedtls_threading_mutex_t lock; +static mbedtls_threading_condition_variable_t ready; +static mbedtls_threading_condition_variable_t start; +static int counter; +static int waiting; +static int go; +/* A thread returns its address on failure */ +static int failed; + +static int StartThreads(pthread_t* t, void* (*f)(void*)) +{ + int n; + + for (n = 0; n < THREADS; n++) { + if (pthread_create(&t[n], NULL, f, NULL) != 0) { + break; + } + } + return n; +} + +/* All n threads started and none failed */ +static int JoinThreads(pthread_t* t, int n) +{ + void* ret; + int ok = (n == THREADS); + int i; + + for (i = 0; i < n; i++) { + if (pthread_join(t[i], &ret) != 0 || ret != NULL) { + ok = 0; + } + } + return ok; +} + +/* A zeroed or freed mutex fails, and a second free does nothing */ +static int MutexUsage(void) +{ + mbedtls_threading_mutex_t m; + int ok; + + memset(&m, 0, sizeof(m)); + ok = mbedtls_mutex_lock(&m) == MBEDTLS_ERR_THREADING_USAGE_ERROR; + mbedtls_mutex_free(&m); + mbedtls_mutex_init(&m); + ok = ok && mbedtls_mutex_lock(&m) == 0 && mbedtls_mutex_unlock(&m) == 0; + mbedtls_mutex_free(&m); + mbedtls_mutex_free(&m); + return ok && + mbedtls_mutex_unlock(&m) == MBEDTLS_ERR_THREADING_USAGE_ERROR; +} + +static void* Count(void* arg) +{ + int i; + + (void)arg; + for (i = 0; i < ROUNDS; i++) { + if (mbedtls_mutex_lock(&lock) != 0) { + return &failed; + } + counter++; + if (mbedtls_mutex_unlock(&lock) != 0) { + return &failed; + } + } + return NULL; +} + +static int Counter(void) +{ + pthread_t t[THREADS]; + + return JoinThreads(t, StartThreads(t, Count)) && + counter == THREADS * ROUNDS; +} + +/* Signals ready, then waits for the broadcast of start */ +static void* Wait(void* arg) +{ + int ret; + + (void)arg; + ret = mbedtls_mutex_lock(&lock); + if (ret == 0) { + waiting++; + ret = mbedtls_condition_variable_signal(&ready); + while (ret == 0 && !go) { + ret = mbedtls_condition_variable_wait(&start, &lock); + } + (void)mbedtls_mutex_unlock(&lock); + } + return (ret == 0) ? NULL : &failed; +} + +static int CondVar(void) +{ + pthread_t t[THREADS]; + int n; + int ret; + + n = StartThreads(t, Wait); + ret = mbedtls_mutex_lock(&lock); + if (ret == 0) { + while (ret == 0 && waiting < n) { + ret = mbedtls_condition_variable_wait(&ready, &lock); + } + go = 1; + if (ret == 0) { + ret = mbedtls_condition_variable_broadcast(&start); + } + (void)mbedtls_mutex_unlock(&lock); + } + return JoinThreads(t, n) && ret == 0; +} + +/* Volatile keys in the shared key store */ +static void* Keys(void* arg) +{ + static const unsigned char key[16] = { 0 }; + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_attributes_t got = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t id; + psa_status_t status = PSA_SUCCESS; + int i; + + (void)arg; + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&attr, PSA_ALG_ECB_NO_PADDING); + psa_set_key_type(&attr, PSA_KEY_TYPE_AES); + for (i = 0; i < KEYS && status == PSA_SUCCESS; i++) { + status = psa_import_key(&attr, key, sizeof(key), &id); + if (status == PSA_SUCCESS) { + status = psa_get_key_attributes(id, &got); + if (status == PSA_SUCCESS && + psa_get_key_type(&got) != PSA_KEY_TYPE_AES) { + status = PSA_ERROR_CORRUPTION_DETECTED; + } + psa_reset_key_attributes(&got); + if (psa_destroy_key(id) != PSA_SUCCESS) { + status = PSA_ERROR_CORRUPTION_DETECTED; + } + } + } + return (status == PSA_SUCCESS) ? NULL : &failed; +} + +static int KeyStore(void) +{ + pthread_t t[THREADS]; + + return JoinThreads(t, StartThreads(t, Keys)); +} + +int main(void) +{ + static const struct { + const char* name; + int (*f)(void); + } cases[] = { + { "mutex usage errors", MutexUsage }, + { "mutex, a counter shared by the threads", Counter }, + { "condition variable, signal and broadcast", CondVar }, + { "PSA keys in each thread", KeyStore } + }; + size_t i; + int fails = 0; + + if (psa_crypto_init() != PSA_SUCCESS) { + printf("FAIL: psa_crypto_init\n"); + return 1; + } + mbedtls_mutex_init(&lock); + if (mbedtls_condition_variable_init(&ready) != 0 || + mbedtls_condition_variable_init(&start) != 0) { + printf("FAIL: mbedtls_condition_variable_init\n"); + return 1; + } + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + if (cases[i].f()) { + printf("PASS: %s\n", cases[i].name); + } + else { + printf("FAIL: %s\n", cases[i].name); + fails++; + } + } + mbedtls_condition_variable_free(&start); + mbedtls_condition_variable_free(&ready); + mbedtls_mutex_free(&lock); + mbedtls_psa_crypto_free(); + return (fails == 0) ? 0 : 1; +} diff --git a/test/psa_server/psa_want_surface_test.c b/test/psa_server/psa_want_surface_test.c new file mode 100644 index 0000000..5e0a362 --- /dev/null +++ b/test/psa_server/psa_want_surface_test.c @@ -0,0 +1,329 @@ +/* psa_want_surface_test.c + * + * The PSA_WANT_* and MBEDTLS_*_C macros seen through psa/crypto.h match what + * the library supports, and buffers of the *_MAX_SIZE macros are large + * enough for it. + * + * 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 + */ + +#include + +#include +#include + +/* A macro is defined when its expansion differs from its name */ +#define STRINGIFY(m) #m +#define EXPANDED(m) STRINGIFY(m) +#define IS_DEFINED(m) (strcmp(EXPANDED(m), #m) != 0) + +#define NOT_CHECKED (-1) +/* A family macro: the library may leave out the parameter set */ +#define FAMILY_WANT(defined) ((defined) ? NOT_CHECKED : 0) + +struct want_case { + const char* name; + psa_key_type_t type; + size_t bits; + psa_algorithm_t alg; + int want; /* the PSA_WANT_* macros of the type and algorithm */ + int feature; /* the MBEDTLS_*_C macros, or NOT_CHECKED */ +}; + +static int failures; + +static psa_status_t run_export(psa_key_id_t key) +{ + uint8_t pub[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE]; + uint8_t pair[PSA_EXPORT_KEY_PAIR_MAX_SIZE]; + size_t len = 0; + psa_status_t status; + + status = psa_export_public_key(key, pub, sizeof(pub), &len); + if (status == PSA_SUCCESS) { + status = psa_export_key(key, pair, sizeof(pair), &len); + } + return status; +} + +static psa_status_t run_sign(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t msg[32]; + uint8_t sig[PSA_SIGNATURE_MAX_SIZE]; + size_t len = 0; + psa_status_t status; + + memset(msg, 0, sizeof(msg)); + status = run_export(key); + if (status == PSA_SUCCESS && PSA_ALG_IS_SIGN_MESSAGE(alg)) { + status = psa_sign_message(key, alg, msg, sizeof(msg), sig, + sizeof(sig), &len); + if (status == PSA_SUCCESS) { + status = psa_verify_message(key, alg, msg, sizeof(msg), sig, len); + } + } + /* Hash-only algorithms such as ECDSA_ANY sign msg as the hash */ + else if (status == PSA_SUCCESS) { + status = psa_sign_hash(key, alg, msg, sizeof(msg), sig, sizeof(sig), + &len); + if (status == PSA_SUCCESS) { + status = psa_verify_hash(key, alg, msg, sizeof(msg), sig, len); + } + } + return status; +} + +static psa_status_t run_ecdh(psa_key_id_t key) +{ + uint8_t pub[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE]; + uint8_t secret[PSA_RAW_KEY_AGREEMENT_OUTPUT_MAX_SIZE]; + size_t pubLen = 0; + size_t len = 0; + psa_status_t status; + + status = run_export(key); + if (status == PSA_SUCCESS) { + status = psa_export_public_key(key, pub, sizeof(pub), &pubLen); + } + if (status == PSA_SUCCESS) { + status = psa_raw_key_agreement(PSA_ALG_ECDH, key, pub, pubLen, secret, + sizeof(secret), &len); + } + return status; +} + +static psa_status_t run_aead(psa_key_id_t key, psa_key_type_t type, + psa_algorithm_t alg) +{ + uint8_t nonce[PSA_AEAD_NONCE_MAX_SIZE]; + uint8_t msg[16]; + uint8_t out[PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(sizeof(msg))]; + size_t len = 0; + + memset(nonce, 0, sizeof(nonce)); + memset(msg, 0, sizeof(msg)); + return psa_aead_encrypt(key, alg, nonce, PSA_AEAD_NONCE_LENGTH(type, alg), + NULL, 0, msg, sizeof(msg), out, sizeof(out), &len); +} + +static psa_status_t run_mac(psa_key_id_t key, psa_algorithm_t alg) +{ + uint8_t msg[16]; + uint8_t mac[PSA_MAC_MAX_SIZE]; + size_t len = 0; + + memset(msg, 0, sizeof(msg)); + return psa_mac_compute(key, alg, msg, sizeof(msg), mac, sizeof(mac), &len); +} + +/* Returns PSA_SUCCESS, or PSA_ERROR_NOT_SUPPORTED when the library does not + * support the case */ +static psa_status_t run_case(const struct want_case* c) +{ + psa_key_attributes_t attr = psa_key_attributes_init(); + psa_key_id_t key = PSA_KEY_ID_NULL; + psa_status_t status; + + psa_set_key_type(&attr, c->type); + psa_set_key_bits(&attr, c->bits); + psa_set_key_algorithm(&attr, c->alg); + if (PSA_ALG_IS_KEY_AGREEMENT(c->alg)) { + psa_set_key_usage_flags(&attr, + PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT); + } + else if (PSA_ALG_IS_AEAD(c->alg)) { + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT); + } + else if (PSA_ALG_IS_MAC(c->alg)) { + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_SIGN_MESSAGE); + } + else if (PSA_ALG_IS_SIGN(c->alg)) { + psa_set_key_usage_flags(&attr, + PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE | + PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | + PSA_KEY_USAGE_EXPORT); + } + else { + psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_EXPORT); + } + + status = psa_generate_key(&attr, &key); + if (status == PSA_SUCCESS) { + if (PSA_ALG_IS_KEY_AGREEMENT(c->alg)) { + status = run_ecdh(key); + } + else if (PSA_ALG_IS_AEAD(c->alg)) { + status = run_aead(key, c->type, c->alg); + } + else if (PSA_ALG_IS_MAC(c->alg)) { + status = run_mac(key, c->alg); + } + else if (PSA_ALG_IS_SIGN(c->alg)) { + status = run_sign(key, c->alg); + } + else { + status = run_export(key); + } + (void)psa_destroy_key(key); + } + return status; +} + +static void check_case(const struct want_case* c) +{ + psa_status_t status = run_case(c); + int supported = (status == PSA_SUCCESS); + + /* Without its PSA_WANT_ macros, a case may fail with any error */ + if (status != PSA_SUCCESS && status != PSA_ERROR_NOT_SUPPORTED && + c->want != 0) { + printf("FAIL %s: status %d\n", c->name, (int)status); + failures++; + } + else if (c->want != NOT_CHECKED && supported != c->want) { + printf("FAIL %s: supported %d, PSA_WANT_ macros %d\n", c->name, + supported, c->want); + failures++; + } + else if (c->feature != NOT_CHECKED && supported != c->feature) { + printf("FAIL %s: supported %d, MBEDTLS_*_C macros %d\n", c->name, + supported, c->feature); + failures++; + } + else { + printf("ok %s: supported %d\n", c->name, supported); + } +} + +int main(void) +{ + const psa_key_type_t ecc = + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1); + const psa_key_type_t k1 = + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_K1); + const psa_key_type_t bp = + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_BRAINPOOL_P_R1); + const psa_key_type_t mont = + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY); + const psa_key_type_t ed = + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS); + const psa_algorithm_t ecdsaAlg = PSA_ALG_ECDSA(PSA_ALG_SHA_256); + const int ecdh = IS_DEFINED(PSA_WANT_ALG_ECDH) && + IS_DEFINED(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE); + const int ecdsa = IS_DEFINED(PSA_WANT_ALG_ECDSA) && + IS_DEFINED(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE); + const int ecdsaAny = IS_DEFINED(PSA_WANT_ALG_ECDSA_ANY) && + IS_DEFINED(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE); + const int rsaGen = IS_DEFINED(PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE); + const int eddsa = IS_DEFINED(PSA_WANT_ALG_PURE_EDDSA) && + IS_DEFINED(PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE); + const int aes = IS_DEFINED(PSA_WANT_KEY_TYPE_AES); + const int aesC = IS_DEFINED(MBEDTLS_AES_C); + const struct want_case cases[] = { + { "secp256r1 ECDH", ecc, 256, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_SECP_R1_256), NOT_CHECKED }, + { "secp384r1 ECDH", ecc, 384, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_SECP_R1_384), NOT_CHECKED }, + { "secp521r1 ECDH", ecc, 521, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_SECP_R1_521), NOT_CHECKED }, + { "secp256r1 ECDSA", ecc, 256, ecdsaAlg, + ecdsa && IS_DEFINED(PSA_WANT_ECC_SECP_R1_256), NOT_CHECKED }, + { "secp384r1 ECDSA", ecc, 384, ecdsaAlg, + ecdsa && IS_DEFINED(PSA_WANT_ECC_SECP_R1_384), NOT_CHECKED }, + { "secp521r1 ECDSA", ecc, 521, ecdsaAlg, + ecdsa && IS_DEFINED(PSA_WANT_ECC_SECP_R1_521), NOT_CHECKED }, + { "secp256r1 ECDSA any", ecc, 256, PSA_ALG_ECDSA_ANY, + ecdsaAny && IS_DEFINED(PSA_WANT_ECC_SECP_R1_256), NOT_CHECKED }, + { "secp256k1 ECDH", k1, 256, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_SECP_K1_256), NOT_CHECKED }, + { "brainpoolP256r1 ECDH", bp, 256, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_BRAINPOOL_P_R1_256), + NOT_CHECKED }, + { "brainpoolP384r1 ECDH", bp, 384, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_BRAINPOOL_P_R1_384), + NOT_CHECKED }, + { "brainpoolP512r1 ECDH", bp, 512, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_BRAINPOOL_P_R1_512), + NOT_CHECKED }, + { "X25519", mont, 255, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_MONTGOMERY_255), NOT_CHECKED }, + { "X448", mont, 448, PSA_ALG_ECDH, + ecdh && IS_DEFINED(PSA_WANT_ECC_MONTGOMERY_448), NOT_CHECKED }, + { "Ed25519", ed, 255, PSA_ALG_PURE_EDDSA, + eddsa && IS_DEFINED(PSA_WANT_ECC_TWISTED_EDWARDS_255), + NOT_CHECKED }, + { "Ed448", ed, 448, PSA_ALG_PURE_EDDSA, + eddsa && IS_DEFINED(PSA_WANT_ECC_TWISTED_EDWARDS_448), + NOT_CHECKED }, + { "AES-128-GCM", PSA_KEY_TYPE_AES, 128, PSA_ALG_GCM, + aes && IS_DEFINED(PSA_WANT_ALG_GCM), + aesC && IS_DEFINED(MBEDTLS_GCM_C) }, + { "AES-128-CCM", PSA_KEY_TYPE_AES, 128, PSA_ALG_CCM, + aes && IS_DEFINED(PSA_WANT_ALG_CCM), + aesC && IS_DEFINED(MBEDTLS_CCM_C) }, + { "ChaCha20-Poly1305", PSA_KEY_TYPE_CHACHA20, 256, + PSA_ALG_CHACHA20_POLY1305, + IS_DEFINED(PSA_WANT_KEY_TYPE_CHACHA20) && + IS_DEFINED(PSA_WANT_ALG_CHACHA20_POLY1305), + IS_DEFINED(MBEDTLS_CHACHAPOLY_C) }, + { "HMAC-SHA-256", PSA_KEY_TYPE_HMAC, 256, + PSA_ALG_HMAC(PSA_ALG_SHA_256), + IS_DEFINED(PSA_WANT_KEY_TYPE_HMAC) && + IS_DEFINED(PSA_WANT_ALG_HMAC), NOT_CHECKED }, + { "ML-KEM-1024 export", PSA_KEY_TYPE_ML_KEM_KEY_PAIR, 1024, + PSA_ALG_ML_KEM, FAMILY_WANT(IS_DEFINED(PSA_WANT_KEY_TYPE_ML_KEM)), + NOT_CHECKED }, + { "ML-DSA-87", PSA_KEY_TYPE_ML_DSA_KEY_PAIR, 256, + PSA_ALG_ML_DSA, FAMILY_WANT(IS_DEFINED(PSA_WANT_KEY_TYPE_ML_DSA)), + NOT_CHECKED }, + { "RSA-2048 PKCS#1 v1.5", PSA_KEY_TYPE_RSA_KEY_PAIR, 2048, + PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_SHA_256), + rsaGen && IS_DEFINED(PSA_WANT_ALG_RSA_PKCS1V15_SIGN), + NOT_CHECKED }, + { "RSA-2048 PKCS#1 v1.5 raw", PSA_KEY_TYPE_RSA_KEY_PAIR, 2048, + PSA_ALG_RSA_PKCS1V15_SIGN_RAW, + rsaGen && IS_DEFINED(PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW), + NOT_CHECKED }, + { "RSA-2048 PSS any salt", PSA_KEY_TYPE_RSA_KEY_PAIR, 2048, + PSA_ALG_RSA_PSS_ANY_SALT(PSA_ALG_SHA_256), + rsaGen && IS_DEFINED(PSA_WANT_ALG_RSA_PSS_ANY_SALT), + NOT_CHECKED }, + }; + size_t i; + psa_status_t status; + + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + printf("FAIL psa_crypto_init status %d\n", (int)status); + return 1; + } + + for (i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) { + check_case(&cases[i]); + } + + mbedtls_psa_crypto_free(); + + if (failures != 0) { + printf("PSA_WANT surface test: FAIL (%d)\n", failures); + return 1; + } + printf("PSA_WANT surface test: OK\n"); + return 0; +} diff --git a/test/wolfcrypt-benchmark/Makefile b/test/wolfcrypt-benchmark/Makefile index 914ad6c..e8402b7 100644 --- a/test/wolfcrypt-benchmark/Makefile +++ b/test/wolfcrypt-benchmark/Makefile @@ -13,7 +13,7 @@ WOLFPSA_DEVID ?= 0x505341 CFLAGS ?= -O2 CPPFLAGS += -I$(WOLFSSL_PATH) -I$(WOLFSSL_PATH)/wolfssl -CPPFLAGS += -I$(WOLFPSA_PATH)/wolfpsa +CPPFLAGS += -I$(WOLFPSA_PATH)/wolfpsa -I$(WOLFPSA_PATH)/build/include CPPFLAGS += -DWOLFSSL_USER_SETTINGS -U HAVE_CONFIG_H CPPFLAGS += -DWOLFSSL_HAVE_PSA CPPFLAGS += -DWOLFPSA_DEVID=$(WOLFPSA_DEVID) diff --git a/tools/gen-crypto-config.py b/tools/gen-crypto-config.py new file mode 100644 index 0000000..008eea8 --- /dev/null +++ b/tools/gen-crypto-config.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2026 wolfSSL Inc. +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Generate the PSA_WANT_ header (crypto_config_zephyr.h for Zephyr, +# crypto_config_generated.h for the Makefile) from the ACTUAL compiled wolfCrypt +# configuration, by preprocessing psa_want_probe.c with the same flags as the +# rest of the build. The result is the consumer-visible PSA_WANT_ surface: +# exactly the algorithm families wolfCrypt has enabled AND wolfPSA implements. +# It also has the TF-PSA-Crypto options the build supports, such as +# MBEDTLS_FS_IO. +# +# Python (not shell) to fit the Zephyr build ecosystem, invoked via CMake's +# PYTHON_EXECUTABLE from wolfPSA's zephyr/CMakeLists.txt, and by the Makefile. +# +# Usage: +# gen-crypto-config.py [preprocessor flags...] +# +# is a path, or a command with arguments such as "ccache gcc". The +# preprocessor flags must reproduce the build's wolfCrypt config -- for +# Zephyr at minimum -DWOLFSSL_USER_SETTINGS -DWOLFSSL_ZEPHYR, the +# wolfssl/wolfpsa include dirs, any -DWOLFSSL_SETTINGS_FILE, and -imacros +# ; for the Makefile build, its CPPFLAGS and CFLAGS. The header is +# written only when its content changes. + +import os +import re +import shlex +import subprocess +import sys + +HEADER = """\ +/* {name} + * + * Copyright (C) 2026 wolfSSL Inc. + * SPDX-License-Identifier: GPL-3.0-or-later + * + * GENERATED at build time by tools/gen-crypto-config.py from the compiled + * wolfCrypt configuration -- DO NOT EDIT and do not check in. It defines the + * consumer-visible PSA_WANT_ surface (what wolfPSA exposes as the PSA API), + * derived from the wolfCrypt features actually enabled in this build intersected + * with what wolfPSA implements. wolfpsa/psa/crypto.h -> crypto_config.h includes + * it, so PSA sizing macros (crypto_sizes.h) and consumer PSA_WANT_-gated code + * track the real build, as do the MBEDTLS_ options it defines. + */ + +#ifndef {guard} +#define {guard} + +""" + +FOOTER = "\n#endif /* {guard} */\n" + +EMIT_TAG = "__WPSA_EMIT__" +WANT_RE = re.compile(r"PSA_WANT_[A-Z0-9_]+") +# TF-PSA-Crypto options, defined without a value as in TF-PSA +OPTION_RE = re.compile(r"MBEDTLS_[A-Z0-9_]+") + + +def main(argv): + if len(argv) < 4: + sys.stderr.write( + "usage: gen-crypto-config.py [flags...]\n") + return 2 + + out_path, probe = argv[1], argv[3] + cc = [argv[2]] if os.path.isfile(argv[2]) else shlex.split(argv[2]) + flags = argv[4:] + + try: + preprocessed = subprocess.run( + [*cc, "-E", "-P", *flags, probe], + check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True).stdout + except subprocess.CalledProcessError as exc: + sys.stderr.write(exc.stderr or "") + sys.stderr.write("gen-crypto-config.py: preprocessing the probe failed\n") + return 1 + + # Collect symbols from the surviving __WPSA_EMIT__ lines only. + emitted = [line for line in preprocessed.splitlines() if EMIT_TAG in line] + wants = sorted({ + match.group(0) + for line in emitted + for match in WANT_RE.finditer(line) + }) + options = sorted({ + match.group(0) + for line in emitted + for match in OPTION_RE.finditer(line) + }) + + if not wants: + sys.stderr.write( + "gen-crypto-config.py: probe produced no PSA_WANT symbols\n") + return 1 + + name = os.path.basename(out_path) + guard = "WOLFPSA_" + re.sub(r"[^A-Z0-9]", "_", name.upper()) + text = (HEADER.format(name=name, guard=guard) + + "".join("#define {} 1\n".format(want) for want in wants) + + "".join("#define {}\n".format(option) for option in options) + + FOOTER.format(guard=guard)) + + # Unchanged content keeps its timestamp, so nothing rebuilds + try: + with open(out_path) as out_file: + if out_file.read() == text: + return 0 + except OSError: + pass + # Replace atomically, so a parallel compile never reads a partial header + tmp_path = out_path + ".tmp" + with open(tmp_path, "w") as out_file: + out_file.write(text) + os.replace(tmp_path, out_path) + + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv)) diff --git a/zephyr/gen/psa_want_probe.c b/tools/psa_want_probe.c similarity index 68% rename from zephyr/gen/psa_want_probe.c rename to tools/psa_want_probe.c index ae4d242..c7fdc00 100644 --- a/zephyr/gen/psa_want_probe.c +++ b/tools/psa_want_probe.c @@ -23,22 +23,22 @@ * NOT a compiled translation unit. This file is *preprocessed* at build time * (gcc -E -P) with the same wolfCrypt configuration as the rest of the build, * and gen-crypto-config.py extracts the surviving `__WPSA_EMIT__` - * lines to generate wolfpsa/psa/crypto_config_zephyr.h -- the consumer-visible + * lines to generate wolfpsa/psa/crypto_config_zephyr.h (Zephyr) or + * crypto_config_generated.h (Makefile) -- the consumer-visible * PSA_WANT_ surface. Each block below maps an enabled wolfCrypt feature to the * PSA_WANT_ symbol(s) wolfPSA exposes for it, so the advertised PSA surface is * exactly "what wolfCrypt compiled" intersected with "what wolfPSA implements". * * Excluded on purpose (wolfPSA has no PSA binding): finite-field DH, PAKE, - * Camellia, ARIA, ECC Brainpool/Koblitz. Keep this in sync with src/ when a - * feature graduates. + * Camellia, ARIA. Keep this in sync with src/ when a feature graduates. */ -/* Pull in the effective wolfCrypt feature configuration (the module's - * user_settings.h + wolfPSA baseline). We include user_settings.h directly - * rather than so this probe preprocesses without - * dragging in the Zephyr kernel headers settings.h pulls under WOLFSSL_ZEPHYR; - * every feature macro this probe tests is materialized by user_settings.h. */ -#include "user_settings.h" +/* The wolfCrypt configuration as src/ sees it. The Zephyr build defines + * __ASSEMBLER__ so settings.h skips the Zephyr kernel headers. */ +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ +#include /* ----- Hashes ----- */ #if !defined(NO_SHA) @@ -127,40 +127,81 @@ __WPSA_EMIT__PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC __WPSA_EMIT__PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_IMPORT __WPSA_EMIT__PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_EXPORT __WPSA_EMIT__PSA_WANT_ALG_RSA_PKCS1V15_SIGN +__WPSA_EMIT__PSA_WANT_ALG_RSA_PKCS1V15_SIGN_RAW __WPSA_EMIT__PSA_WANT_ALG_RSA_PKCS1V15_CRYPT #if defined(WOLFSSL_KEY_GEN) __WPSA_EMIT__PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_GENERATE #endif #if defined(WC_RSA_PSS) __WPSA_EMIT__PSA_WANT_ALG_RSA_PSS +#if defined(WOLFSSL_PSS_SALT_LEN_DISCOVER) +__WPSA_EMIT__PSA_WANT_ALG_RSA_PSS_ANY_SALT +#endif #endif #if defined(WOLFSSL_RSA_OAEP) || defined(WC_RSA_OAEP) __WPSA_EMIT__PSA_WANT_ALG_RSA_OAEP #endif #endif /* !NO_RSA */ -/* ----- ECC (secp r1) ----- */ -#if defined(HAVE_ECC) +/* ----- ECC key types, for every curve family ----- */ +#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) || \ + defined(HAVE_ED25519) || defined(HAVE_ED448) __WPSA_EMIT__PSA_WANT_KEY_TYPE_ECC_PUBLIC_KEY __WPSA_EMIT__PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_BASIC __WPSA_EMIT__PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT __WPSA_EMIT__PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_EXPORT __WPSA_EMIT__PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_GENERATE +#endif +#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) +__WPSA_EMIT__PSA_WANT_ALG_ECDH +#endif + +/* ----- ECC (secp, Koblitz, Brainpool) ----- */ +#if defined(HAVE_ECC) __WPSA_EMIT__PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_DERIVE __WPSA_EMIT__PSA_WANT_ALG_ECDSA -__WPSA_EMIT__PSA_WANT_ALG_ECDH +__WPSA_EMIT__PSA_WANT_ALG_ECDSA_ANY #if defined(WOLFSSL_ECDSA_DETERMINISTIC_K) __WPSA_EMIT__PSA_WANT_ALG_DETERMINISTIC_ECDSA #endif -#if !defined(NO_ECC256) +/* The curve sizes as wolfCrypt's ecc.c enables them */ +#if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 +#define WPSA_ECC256 +#endif +#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384 +#define WPSA_ECC384 +#endif +#if (defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 512 +#define WPSA_ECC512 +#endif +#if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521 +#define WPSA_ECC521 +#endif +#if !defined(NO_ECC_SECP) +#if defined(WPSA_ECC256) __WPSA_EMIT__PSA_WANT_ECC_SECP_R1_256 #endif -#if defined(HAVE_ECC384) +#if defined(WPSA_ECC384) __WPSA_EMIT__PSA_WANT_ECC_SECP_R1_384 #endif -#if defined(HAVE_ECC521) +#if defined(WPSA_ECC521) __WPSA_EMIT__PSA_WANT_ECC_SECP_R1_521 #endif +#endif /* !NO_ECC_SECP */ +#if defined(HAVE_ECC_KOBLITZ) && defined(WPSA_ECC256) +__WPSA_EMIT__PSA_WANT_ECC_SECP_K1_256 +#endif +#if defined(HAVE_ECC_BRAINPOOL) +#if defined(WPSA_ECC256) +__WPSA_EMIT__PSA_WANT_ECC_BRAINPOOL_P_R1_256 +#endif +#if defined(WPSA_ECC384) +__WPSA_EMIT__PSA_WANT_ECC_BRAINPOOL_P_R1_384 +#endif +#if defined(WPSA_ECC512) +__WPSA_EMIT__PSA_WANT_ECC_BRAINPOOL_P_R1_512 +#endif +#endif /* HAVE_ECC_BRAINPOOL */ #endif /* HAVE_ECC */ /* ----- Montgomery curves ----- */ @@ -171,6 +212,19 @@ __WPSA_EMIT__PSA_WANT_ECC_MONTGOMERY_255 __WPSA_EMIT__PSA_WANT_ECC_MONTGOMERY_448 #endif +/* ----- Edwards curves ----- */ +#if defined(HAVE_ED25519) +__WPSA_EMIT__PSA_WANT_ECC_TWISTED_EDWARDS_255 +__WPSA_EMIT__PSA_WANT_ALG_ED25519PH +#endif +#if defined(HAVE_ED448) +__WPSA_EMIT__PSA_WANT_ECC_TWISTED_EDWARDS_448 +__WPSA_EMIT__PSA_WANT_ALG_ED448PH +#endif +#if defined(HAVE_ED25519) || defined(HAVE_ED448) +__WPSA_EMIT__PSA_WANT_ALG_PURE_EDDSA +#endif + /* ----- KDFs ----- */ #if defined(HAVE_HKDF) __WPSA_EMIT__PSA_WANT_ALG_HKDF @@ -203,3 +257,13 @@ __WPSA_EMIT__PSA_WANT_KEY_TYPE_ML_KEM #if defined(WOLFSSL_HAVE_MLDSA) __WPSA_EMIT__PSA_WANT_KEY_TYPE_ML_DSA #endif + +/* ----- TF-PSA-Crypto options ----- */ +#if !defined(NO_FILESYSTEM) +__WPSA_EMIT__MBEDTLS_FS_IO +#endif +/* The PSA RNG, wolfCrypt's DRBG. No public API in TF-PSA-Crypto 1.x. */ +#if !defined(WC_NO_RNG) +__WPSA_EMIT__MBEDTLS_ENTROPY_C +__WPSA_EMIT__MBEDTLS_CTR_DRBG_C +#endif diff --git a/user_settings.h b/user_settings.h index 5bef3d8..0edd1d9 100644 --- a/user_settings.h +++ b/user_settings.h @@ -34,8 +34,16 @@ #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 +#endif /* MBEDTLS_THREADING_C && !WOLFPSA_THREAD_SAFE */ +#ifndef WOLFPSA_THREAD_SAFE #define SINGLE_THREADED +#endif /* WOLFPSA_THREAD_SAFE */ #define WOLFSSL_PSA_ENGINE #define WOLFSSL_SP_MATH_ALL #define WOLFSSL_HAVE_SP_RSA @@ -96,6 +104,10 @@ * emit the payload from psa_aead_update() instead of buffering it all for * finish(). */ #define WOLFSSL_AESGCM_STREAM +/* PSA allows 4 and 8 byte GCM tags; wolfCrypt's default minimum is 12 */ +#ifndef WOLFSSL_MIN_AUTH_TAG_SZ +#define WOLFSSL_MIN_AUTH_TAG_SZ 4 +#endif #define HAVE_AESCCM #define HAVE_AES_ECB #define WOLFSSL_AES_COUNTER @@ -126,6 +138,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 MBEDTLS_X509_BADCERT_MISSING, see 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 8833f8d..420513b 100644 --- a/wolfpsa.map +++ b/wolfpsa.map @@ -1,5 +1,12 @@ WOLFPSA_1.0 { global: + mbedtls_condition_variable_*; + mbedtls_mutex_*; + mbedtls_pk_can_do_psa; + mbedtls_pk_free; + mbedtls_pk_init; + mbedtls_pk_parse_key; + mbedtls_pk_parse_keyfile; mbedtls_platform_zeroize; mbedtls_psa_crypto_free; psa_aead_abort; @@ -136,6 +143,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 0000000..b7aa67d --- /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 0000000..d5bc4f5 --- /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 0000000..eaa0c28 --- /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 0000000..b1f33ba --- /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 0000000..b4131ea --- /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/pk.h b/wolfpsa/mbedtls/pk.h new file mode 100644 index 0000000..7cdb50f --- /dev/null +++ b/wolfpsa/mbedtls/pk.h @@ -0,0 +1,75 @@ +/* pk.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_PK_H +#define MBEDTLS_PK_H + +#include "tf-psa-crypto/build_info.h" +#include "psa/crypto.h" + +#include + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/* TF-PSA-Crypto returns PSA_ERROR_INVALID_ARGUMENT and + * PSA_ERROR_INSUFFICIENT_MEMORY for bad input and allocation failures */ +#define MBEDTLS_ERR_PK_TYPE_MISMATCH -0x3F00 +#define MBEDTLS_ERR_PK_FILE_IO_ERROR -0x3E00 +#define MBEDTLS_ERR_PK_KEY_INVALID_VERSION -0x3D80 +#define MBEDTLS_ERR_PK_KEY_INVALID_FORMAT -0x3D00 +#define MBEDTLS_ERR_PK_UNKNOWN_PK_ALG -0x3C80 +#define MBEDTLS_ERR_PK_PASSWORD_REQUIRED -0x3C00 +#define MBEDTLS_ERR_PK_PASSWORD_MISMATCH -0x3B80 +#define MBEDTLS_ERR_PK_INVALID_PUBKEY -0x3B00 +#define MBEDTLS_ERR_PK_INVALID_ALG -0x3A80 +#define MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE -0x3A00 +#define MBEDTLS_ERR_PK_FEATURE_UNAVAILABLE -0x3980 + +#if defined(PSA_WANT_ALG_DETERMINISTIC_ECDSA) + #define MBEDTLS_PK_ALG_ECDSA(hash_alg) PSA_ALG_DETERMINISTIC_ECDSA(hash_alg) +#else + #define MBEDTLS_PK_ALG_ECDSA(hash_alg) PSA_ALG_ECDSA(hash_alg) +#endif /* PSA_WANT_ALG_DETERMINISTIC_ECDSA */ + +/* An RSA or EC private key, DER */ +typedef struct mbedtls_pk_context { + unsigned char* der; + size_t der_len; +} mbedtls_pk_context; + +void mbedtls_pk_init(mbedtls_pk_context* ctx); +void mbedtls_pk_free(mbedtls_pk_context* ctx); +int mbedtls_pk_parse_key(mbedtls_pk_context* ctx, const unsigned char* key, + size_t keylen, const unsigned char* pwd, size_t pwdlen); +int mbedtls_pk_can_do_psa(const mbedtls_pk_context* pk, psa_algorithm_t alg, + psa_key_usage_t usage); +#if defined(MBEDTLS_FS_IO) +int mbedtls_pk_parse_keyfile(mbedtls_pk_context* ctx, const char* path, + const char* password); +#endif /* MBEDTLS_FS_IO */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MBEDTLS_PK_H */ diff --git a/wolfpsa/mbedtls/psa_util.h b/wolfpsa/mbedtls/psa_util.h new file mode 100644 index 0000000..f30228a --- /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 0000000..3cd4a0c --- /dev/null +++ b/wolfpsa/mbedtls/ssl.h @@ -0,0 +1,174 @@ +/* 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_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; + +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 */ + /* Kept, not called yet */ + 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; + int authmode; + uint32_t verify_flags; /* MBEDTLS_X509_BADCERT_* from the handshake */ + int peer_cert; /* the peer sent a certificate */ +} 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); +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/threading.h b/wolfpsa/mbedtls/threading.h new file mode 100644 index 0000000..6815411 --- /dev/null +++ b/wolfpsa/mbedtls/threading.h @@ -0,0 +1,82 @@ +/* threading.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 + */ + +/* TF-PSA-Crypto 1.x mutexes and condition variables, on pthreads. Built with + * MBEDTLS_THREADING_C=1. */ + +#ifndef MBEDTLS_THREADING_H +#define MBEDTLS_THREADING_H + +#include "tf-psa-crypto/build_info.h" + +#if defined(MBEDTLS_THREADING_PTHREAD) + #include +#endif /* MBEDTLS_THREADING_PTHREAD */ + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +#define MBEDTLS_ERR_THREADING_USAGE_ERROR -0x001E +#define MBEDTLS_ERR_THREADING_MUTEX_ERROR MBEDTLS_ERR_THREADING_USAGE_ERROR + +#if defined(MBEDTLS_THREADING_C) + +#if defined(MBEDTLS_THREADING_PTHREAD) +typedef pthread_mutex_t mbedtls_platform_mutex_t; +typedef pthread_cond_t mbedtls_platform_condition_variable_t; +#else + #error "MBEDTLS_THREADING_C is supported with pthreads only" +#endif /* MBEDTLS_THREADING_PTHREAD */ + +typedef struct mbedtls_threading_mutex_t { + mbedtls_platform_mutex_t mutex; + char initialized; +} mbedtls_threading_mutex_t; + +typedef struct mbedtls_threading_condition_variable_t { + mbedtls_platform_condition_variable_t cond; +} mbedtls_threading_condition_variable_t; + +void mbedtls_mutex_init(mbedtls_threading_mutex_t* mutex); +void mbedtls_mutex_free(mbedtls_threading_mutex_t* mutex); +int mbedtls_mutex_lock(mbedtls_threading_mutex_t* mutex); +int mbedtls_mutex_unlock(mbedtls_threading_mutex_t* mutex); + +int mbedtls_condition_variable_init( + mbedtls_threading_condition_variable_t* cond); +void mbedtls_condition_variable_free( + mbedtls_threading_condition_variable_t* cond); +int mbedtls_condition_variable_signal( + mbedtls_threading_condition_variable_t* cond); +int mbedtls_condition_variable_broadcast( + mbedtls_threading_condition_variable_t* cond); +int mbedtls_condition_variable_wait( + mbedtls_threading_condition_variable_t* cond, + mbedtls_threading_mutex_t* mutex); + +#endif /* MBEDTLS_THREADING_C */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MBEDTLS_THREADING_H */ diff --git a/wolfpsa/mbedtls/timing.h b/wolfpsa/mbedtls/timing.h new file mode 100644 index 0000000..5c21245 --- /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 0000000..d60952e --- /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 0000000..d15e8bc --- /dev/null +++ b/wolfpsa/mbedtls/x509.h @@ -0,0 +1,59 @@ +/* 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_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 0000000..aefebf9 --- /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 */ diff --git a/wolfpsa/psa/crypto.h b/wolfpsa/psa/crypto.h index f708a95..df21230 100644 --- a/wolfpsa/psa/crypto.h +++ b/wolfpsa/psa/crypto.h @@ -30,6 +30,8 @@ #include "crypto_types.h" #include "crypto_values.h" #include "crypto_config.h" +/* MBEDTLS_*_C feature macros, which TF-PSA-Crypto's psa/crypto.h includes */ +#include "tf-psa-crypto/build_info.h" #include "crypto_sizes.h" #include "crypto_struct.h" #include "crypto-pqc.h" diff --git a/wolfpsa/psa/crypto_config.h b/wolfpsa/psa/crypto_config.h index e12d1ee..7971087 100644 --- a/wolfpsa/psa/crypto_config.h +++ b/wolfpsa/psa/crypto_config.h @@ -24,28 +24,13 @@ #if defined(CONFIG_WOLFPSA) -/* Zephyr build: derive the PSA_WANT_* surface from the consumer's Kconfig - * (CONFIG_PSA_WANT_*) instead of the small hardcoded list below, so that the - * size macros in crypto_sizes.h and any PSA_WANT_*-gated consumer code match - * whatever capabilities the application enabled. */ +/* Generated by the Zephyr build, in zephyr/CMakeLists.txt */ #include "crypto_config_zephyr.h" #else /* standalone (Makefile) build */ -#define PSA_WANT_ALG_SHA_1 1 -#define PSA_WANT_ALG_SHA_224 1 -#define PSA_WANT_ALG_SHA_256 1 -#define PSA_WANT_ALG_SHA_384 1 -#define PSA_WANT_ALG_SHA_512 1 - -#define PSA_WANT_KEY_TYPE_RSA_KEY_PAIR_BASIC 1 -#define PSA_WANT_KEY_TYPE_RSA_PUBLIC_KEY 1 - -#define PSA_WANT_KEY_TYPE_ML_DSA 1 -#define PSA_WANT_KEY_TYPE_ML_KEM 1 - -/* TF-PSA-Crypto platform option: the platform has stdio files */ -#define MBEDTLS_FS_IO +/* Generated by the Makefile */ +#include "crypto_config_generated.h" #endif /* CONFIG_WOLFPSA */ diff --git a/wolfpsa/psa/crypto_extra.h b/wolfpsa/psa/crypto_extra.h index 1f19484..a57fa72 100644 --- a/wolfpsa/psa/crypto_extra.h +++ b/wolfpsa/psa/crypto_extra.h @@ -58,7 +58,8 @@ static inline int mbedtls_svc_key_id_is_null(mbedtls_svc_key_id_t key) /* Destroy all volatile keys and return to the state before psa_crypto_init(). * Not thread safe: call when no other PSA call is in progress. Also - * unregisters crypto callbacks; register them again after psa_crypto_init(). */ + * unregisters crypto callbacks when PSA held the last wolfCrypt_Init() + * reference; register them again after psa_crypto_init(). */ void mbedtls_psa_crypto_free(void); #ifdef __cplusplus diff --git a/wolfpsa/psa/crypto_sizes.h b/wolfpsa/psa/crypto_sizes.h index 5c17dea..837088b 100644 --- a/wolfpsa/psa/crypto_sizes.h +++ b/wolfpsa/psa/crypto_sizes.h @@ -151,6 +151,8 @@ #define PSA_VENDOR_ECC_MAX_CURVE_BITS 512u #elif defined(PSA_WANT_ECC_MONTGOMERY_448) #define PSA_VENDOR_ECC_MAX_CURVE_BITS 448u +#elif defined(PSA_WANT_ECC_TWISTED_EDWARDS_448) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 448u #elif defined(PSA_WANT_ECC_SECP_R1_384) #define PSA_VENDOR_ECC_MAX_CURVE_BITS 384u #elif defined(PSA_WANT_ECC_BRAINPOOL_P_R1_384) @@ -163,6 +165,8 @@ #define PSA_VENDOR_ECC_MAX_CURVE_BITS 256u #elif defined(PSA_WANT_ECC_MONTGOMERY_255) #define PSA_VENDOR_ECC_MAX_CURVE_BITS 255u +#elif defined(PSA_WANT_ECC_TWISTED_EDWARDS_255) +#define PSA_VENDOR_ECC_MAX_CURVE_BITS 255u #else #define PSA_VENDOR_ECC_MAX_CURVE_BITS 0u #endif @@ -270,9 +274,14 @@ (PSA_BITS_TO_BYTES(curve_bits) * 2u) +/* ECDSA or EdDSA: an Ed448 signature is 114 bytes, not 2 * 56 */ +#define PSA_VENDOR_ECC_SIGNATURE_SIZE(key_bits) \ + ((key_bits) == 448u ? 114u : PSA_ECDSA_SIGNATURE_SIZE(key_bits)) + + #define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ (PSA_KEY_TYPE_IS_RSA(key_type) ? ((void) alg, PSA_BITS_TO_BYTES(key_bits)) : \ - PSA_KEY_TYPE_IS_ECC(key_type) ? PSA_ECDSA_SIGNATURE_SIZE(key_bits) : \ + PSA_KEY_TYPE_IS_ECC(key_type) ? PSA_VENDOR_ECC_SIGNATURE_SIZE(key_bits) : \ PSA_KEY_TYPE_IS_ML_DSA(key_type) ? \ ((void) alg, \ (key_bits) == 128u ? 2420u : \ @@ -291,6 +300,15 @@ #undef PSA_SIGNATURE_MAX_SIZE #define PSA_SIGNATURE_MAX_SIZE PSA_VENDOR_ECDSA_SIGNATURE_MAX_SIZE #endif +#if defined(PSA_WANT_ECC_TWISTED_EDWARDS_448) && \ + (PSA_VENDOR_ECC_SIGNATURE_SIZE(448u) > PSA_SIGNATURE_MAX_SIZE) +#undef PSA_SIGNATURE_MAX_SIZE +#define PSA_SIGNATURE_MAX_SIZE PSA_VENDOR_ECC_SIGNATURE_SIZE(448u) +#elif defined(PSA_WANT_ECC_TWISTED_EDWARDS_255) && \ + (PSA_VENDOR_ECC_SIGNATURE_SIZE(255u) > PSA_SIGNATURE_MAX_SIZE) +#undef PSA_SIGNATURE_MAX_SIZE +#define PSA_SIGNATURE_MAX_SIZE PSA_VENDOR_ECC_SIGNATURE_SIZE(255u) +#endif #if (defined(PSA_WANT_ALG_RSA_PKCS1V15_SIGN) || defined(PSA_WANT_ALG_RSA_PSS)) && \ (PSA_BITS_TO_BYTES(PSA_VENDOR_RSA_MAX_KEY_BITS) > PSA_SIGNATURE_MAX_SIZE) #undef PSA_SIGNATURE_MAX_SIZE @@ -300,6 +318,16 @@ #undef PSA_SIGNATURE_MAX_SIZE #define PSA_SIGNATURE_MAX_SIZE 4627u #endif +/* Ed448 (114 bytes) and Ed25519 (64 bytes) signatures */ +#if defined(PSA_WANT_ECC_TWISTED_EDWARDS_448) && \ + (114u > PSA_SIGNATURE_MAX_SIZE) +#undef PSA_SIGNATURE_MAX_SIZE +#define PSA_SIGNATURE_MAX_SIZE 114u +#elif defined(PSA_WANT_ECC_TWISTED_EDWARDS_255) && \ + (64u > PSA_SIGNATURE_MAX_SIZE) +#undef PSA_SIGNATURE_MAX_SIZE +#define PSA_SIGNATURE_MAX_SIZE 64u +#endif #define PSA_ASYMMETRIC_ENCRYPT_OUTPUT_SIZE(key_type, key_bits, alg) \ @@ -348,7 +376,7 @@ #define PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits) \ - (PSA_BITS_TO_BYTES(key_bits)) + ((key_bits) == 448u ? 57u : PSA_BITS_TO_BYTES(key_bits)) #define PSA_KEY_EXPORT_FFDH_KEY_PAIR_MAX_SIZE(key_bits) \ @@ -366,6 +394,8 @@ (key_type) == PSA_KEY_TYPE_RSA_PUBLIC_KEY ? PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \ (key_type) == PSA_KEY_TYPE_DSA_KEY_PAIR ? PSA_KEY_EXPORT_DSA_KEY_PAIR_MAX_SIZE(key_bits) : \ (key_type) == PSA_KEY_TYPE_DSA_PUBLIC_KEY ? PSA_KEY_EXPORT_DSA_PUBLIC_KEY_MAX_SIZE(key_bits) : \ + (key_type) == PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY) ? \ + PSA_BITS_TO_BYTES(key_bits) : \ PSA_KEY_TYPE_IS_ECC_KEY_PAIR(key_type) ? PSA_KEY_EXPORT_ECC_KEY_PAIR_MAX_SIZE(key_bits) : \ PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(key_type) ? PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) : \ /* ML-DSA: key-pair export is the 32-byte seed; public key export by parameter set */ \ @@ -423,6 +453,15 @@ #define PSA_EXPORT_KEY_PAIR_MAX_SIZE \ PSA_KEY_EXPORT_FFDH_KEY_PAIR_MAX_SIZE(PSA_VENDOR_FFDH_MAX_KEY_BITS) #endif +/* ML-KEM and ML-DSA key pairs export as their 64 and 32 byte seeds */ +#if defined(PSA_WANT_KEY_TYPE_ML_KEM) && (64u > PSA_EXPORT_KEY_PAIR_MAX_SIZE) +#undef PSA_EXPORT_KEY_PAIR_MAX_SIZE +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE 64u +#endif +#if defined(PSA_WANT_KEY_TYPE_ML_DSA) && (32u > PSA_EXPORT_KEY_PAIR_MAX_SIZE) +#undef PSA_EXPORT_KEY_PAIR_MAX_SIZE +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE 32u +#endif #define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE 1 @@ -453,6 +492,12 @@ #undef PSA_EXPORT_PUBLIC_KEY_MAX_SIZE #define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE 2592u #endif +/* ML-KEM-1024 public key (1568 bytes) */ +#if defined(PSA_WANT_KEY_TYPE_ML_KEM) && \ + (1568u > PSA_EXPORT_PUBLIC_KEY_MAX_SIZE) +#undef PSA_EXPORT_PUBLIC_KEY_MAX_SIZE +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE 1568u +#endif #define PSA_EXPORT_KEY_PAIR_OR_PUBLIC_MAX_SIZE \ ((PSA_EXPORT_KEY_PAIR_MAX_SIZE > PSA_EXPORT_PUBLIC_KEY_MAX_SIZE) ? \ diff --git a/wolfpsa/psa/crypto_struct.h b/wolfpsa/psa/crypto_struct.h index 510dbf4..b684aac 100644 --- a/wolfpsa/psa/crypto_struct.h +++ b/wolfpsa/psa/crypto_struct.h @@ -282,6 +282,19 @@ static inline psa_algorithm_t psa_get_key_algorithm( return attributes->policy.alg; } +/* Mbed TLS second permitted algorithm, honored with WOLFPSA_ENROLLMENT_ALG */ +static inline void psa_set_key_enrollment_algorithm( + psa_key_attributes_t *attributes, psa_algorithm_t alg2) +{ + attributes->policy.alg2 = alg2; +} + +static inline psa_algorithm_t psa_get_key_enrollment_algorithm( + const psa_key_attributes_t *attributes) +{ + return attributes->policy.alg2; +} + static inline void psa_set_key_type(psa_key_attributes_t *attributes, psa_key_type_t type) { diff --git a/wolfpsa/psa/crypto_values.h b/wolfpsa/psa/crypto_values.h index 2560885..8cf09d3 100644 --- a/wolfpsa/psa/crypto_values.h +++ b/wolfpsa/psa/crypto_values.h @@ -635,8 +635,7 @@ #define PSA_ALG_IS_HASH_AND_SIGN(alg) \ - (PSA_ALG_IS_RSA_PSS(alg) || PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) || \ - PSA_ALG_IS_ECDSA(alg) || PSA_ALG_IS_HASH_EDDSA(alg)) + (PSA_ALG_IS_SIGN_HASH(alg) && ((alg) & PSA_ALG_HASH_MASK) != 0) #define PSA_ALG_SIGN_GET_HASH(alg) \ diff --git a/wolfpsa/psa_key_storage.h b/wolfpsa/psa_key_storage.h index 036b31d..4caf1df 100644 --- a/wolfpsa/psa_key_storage.h +++ b/wolfpsa/psa_key_storage.h @@ -100,6 +100,8 @@ WOLFSSL_API psa_status_t wolfpsa_get_key_data(psa_key_id_t key_id, size_t* key_data_length); WOLFSSL_API void wolfpsa_forcezero_free_key_data(uint8_t* key_data, size_t key_data_length); +WOLFSSL_LOCAL int wolfpsa_key_permits_alg( + const psa_key_attributes_t* attributes, psa_algorithm_t alg); #ifdef __cplusplus } diff --git a/wolfpsa/tf-psa-crypto/build_info.h b/wolfpsa/tf-psa-crypto/build_info.h index 7da0f57..2f01839 100644 --- a/wolfpsa/tf-psa-crypto/build_info.h +++ b/wolfpsa/tf-psa-crypto/build_info.h @@ -30,9 +30,10 @@ #define MBEDTLS_PSA_CRYPTO_C #define MBEDTLS_PSA_CRYPTO_CLIENT -/* The PSA RNG, wolfCrypt's DRBG. No public API in TF-PSA-Crypto 1.x. */ -#define MBEDTLS_ENTROPY_C -#define MBEDTLS_CTR_DRBG_C +/* MBEDTLS_ENTROPY_C and MBEDTLS_CTR_DRBG_C come from crypto_config.h */ +/* mbedtls/pk.h: private keys, PEM or DER */ +#define MBEDTLS_PK_C +#define MBEDTLS_PK_PARSE_C #if defined(PSA_WANT_KEY_TYPE_AES) #define MBEDTLS_AES_C @@ -56,4 +57,10 @@ #define MBEDTLS_CIPHER_MODE_CTR #endif /* PSA_WANT_ALG_CTR */ +/* pthreads, the one implementation of MBEDTLS_THREADING_C */ +#if defined(MBEDTLS_THREADING_C) && !defined(MBEDTLS_THREADING_PTHREAD) && \ + !defined(__ZEPHYR__) + #define MBEDTLS_THREADING_PTHREAD +#endif /* MBEDTLS_THREADING_C && !MBEDTLS_THREADING_PTHREAD && !__ZEPHYR__ */ + #endif /* TF_PSA_CRYPTO_BUILD_INFO_H */ diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 37426e1..da65dec 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -35,7 +35,7 @@ if(CONFIG_WOLFPSA) # wolfPSA exposes as the PSA API exactly the crypto that the user's wolfCrypt # configuration enabled (and that wolfPSA implements). We derive the # consumer-visible PSA_WANT_ surface from the ACTUAL compiled config by - # preprocessing zephyr/gen/psa_want_probe.c and generating + # preprocessing tools/psa_want_probe.c and generating # crypto_config_zephyr.h, which -> crypto_config.h includes. # Generated at CONFIGURE time (autoconf.h already exists) so it is present # before any TU that includes compiles. @@ -53,13 +53,14 @@ if(CONFIG_WOLFPSA) set(WOLFPSA_GEN_DIR ${CMAKE_CURRENT_BINARY_DIR}/wolfpsa_gen) set(WOLFPSA_GEN_HDR ${WOLFPSA_GEN_DIR}/crypto_config_zephyr.h) - set(WOLFPSA_PROBE ${WOLFPSA_DIR}/zephyr/gen/psa_want_probe.c) - set(WOLFPSA_GEN_PY ${WOLFPSA_DIR}/zephyr/gen/gen-crypto-config.py) + set(WOLFPSA_PROBE ${WOLFPSA_DIR}/tools/psa_want_probe.c) + set(WOLFPSA_GEN_PY ${WOLFPSA_DIR}/tools/gen-crypto-config.py) file(MAKE_DIRECTORY ${WOLFPSA_GEN_DIR}) + # __ASSEMBLER__: settings.h then skips the Zephyr kernel headers set(_wolfpsa_probe_flags - -D__ZEPHYR__=1 -DWOLFSSL_USER_SETTINGS -DWOLFSSL_ZEPHYR - -I${WOLFPSA_DIR} -I${WOLFPSA_DIR}/wolfpsa + -D__ZEPHYR__=1 -D__ASSEMBLER__ -DWOLFSSL_USER_SETTINGS -DWOLFSSL_ZEPHYR + -I${_wolfssl_mod_dir} -I${WOLFPSA_DIR} -I${WOLFPSA_DIR}/wolfpsa -I${_wolfssl_mod_dir}/zephyr -imacros ${_wolfpsa_autoconf}) # If the user supplied their own wolfCrypt config, the probe must see it too. @@ -124,6 +125,9 @@ if(CONFIG_WOLFPSA) # Image-wide: a settings file picking wolfCrypt's AES core may read it too. zephyr_compile_definitions_ifdef(CONFIG_WOLFPSA_AES_FAST WOLFPSA_AES_FAST) + zephyr_compile_definitions_ifdef(CONFIG_WOLFPSA_ENROLLMENT_ALG + WOLFPSA_ENROLLMENT_ALG) + # Inherit WOLFSSL_USER_SETTINGS + WOLFSSL_ZEPHYR and the wolfcrypt include # dirs from the wolfSSL module's interface library. zephyr_library_link_libraries(wolfSSL) diff --git a/zephyr/Kconfig b/zephyr/Kconfig index f607c6f..5ad44d5 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -76,6 +76,14 @@ config WOLFPSA_AES_FAST channel is out of scope - a part with no data cache, or an attacker with no way to measure it. +config WOLFPSA_ENROLLMENT_ALG + bool "Mbed TLS enrollment algorithm on volatile keys" + help + Define WOLFPSA_ENROLLMENT_ALG: a volatile key may have a second + permitted algorithm, set with psa_set_key_enrollment_algorithm(), as + in Mbed TLS. Using one key with two algorithms is not recommended, so + enable this only for code that sets one, such as hostap. + config WOLFPSA_THREAD_SAFE bool "Serialize wolfPSA shared state with a global mutex" default y diff --git a/zephyr/gen/gen-crypto-config.py b/zephyr/gen/gen-crypto-config.py deleted file mode 100644 index e64966d..0000000 --- a/zephyr/gen/gen-crypto-config.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2026 wolfSSL Inc. -# SPDX-License-Identifier: GPL-3.0-or-later -# -# Generate wolfpsa/psa/crypto_config_zephyr.h from the ACTUAL compiled wolfCrypt -# configuration, by preprocessing psa_want_probe.c with the same flags as the -# rest of the build. The result is the consumer-visible PSA_WANT_ surface: -# exactly the algorithm families wolfCrypt has enabled AND wolfPSA implements. -# -# Python (not shell) to fit the Zephyr build ecosystem, invoked via CMake's -# PYTHON_EXECUTABLE from wolfPSA's zephyr/CMakeLists.txt. -# -# Usage: -# gen-crypto-config.py [preprocessor flags...] -# -# The preprocessor flags must reproduce the build's wolfCrypt config -- at -# minimum -DWOLFSSL_USER_SETTINGS -DWOLFSSL_ZEPHYR, the wolfssl/wolfpsa include -# dirs, any -DWOLFSSL_SETTINGS_FILE, and -imacros . - -import re -import subprocess -import sys - -HEADER = """\ -/* crypto_config_zephyr.h - * - * Copyright (C) 2026 wolfSSL Inc. - * SPDX-License-Identifier: GPL-3.0-or-later - * - * GENERATED at build time by zephyr/gen/gen-crypto-config.py from the compiled - * wolfCrypt configuration -- DO NOT EDIT and do not check in. It defines the - * consumer-visible PSA_WANT_ surface (what wolfPSA exposes as the PSA API), - * derived from the wolfCrypt features actually enabled in this build intersected - * with what wolfPSA implements. wolfpsa/psa/crypto.h -> crypto_config.h includes - * it under CONFIG_WOLFPSA, so PSA sizing macros (crypto_sizes.h) and consumer - * PSA_WANT_-gated code track the real build. - */ - -#ifndef WOLFPSA_CRYPTO_CONFIG_ZEPHYR_H -#define WOLFPSA_CRYPTO_CONFIG_ZEPHYR_H - -""" - -FOOTER = "\n#endif /* WOLFPSA_CRYPTO_CONFIG_ZEPHYR_H */\n" - -EMIT_TAG = "__WPSA_EMIT__" -WANT_RE = re.compile(r"PSA_WANT_[A-Z0-9_]+") - - -def main(argv): - if len(argv) < 4: - sys.stderr.write( - "usage: gen-crypto-config.py [flags...]\n") - return 2 - - out_path, cc, probe = argv[1], argv[2], argv[3] - flags = argv[4:] - - try: - preprocessed = subprocess.run( - [cc, "-E", "-P", *flags, probe], - check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True).stdout - except subprocess.CalledProcessError as exc: - sys.stderr.write(exc.stderr or "") - sys.stderr.write("gen-crypto-config.py: preprocessing the probe failed\n") - return 1 - - # Collect PSA_WANT_ symbols from the surviving __WPSA_EMIT__ lines only. - wants = sorted({ - match.group(0) - for line in preprocessed.splitlines() if EMIT_TAG in line - for match in WANT_RE.finditer(line) - }) - - if not wants: - sys.stderr.write( - "gen-crypto-config.py: probe produced no PSA_WANT symbols\n") - return 1 - - with open(out_path, "w") as out_file: - out_file.write(HEADER) - for want in wants: - out_file.write("#define {} 1\n".format(want)) - out_file.write(FOOTER) - - return 0 - - -if __name__ == "__main__": - sys.exit(main(sys.argv)) diff --git a/zephyr/user_settings_example.h b/zephyr/user_settings_example.h index eb570a2..1db78ea 100644 --- a/zephyr/user_settings_example.h +++ b/zephyr/user_settings_example.h @@ -143,6 +143,10 @@ extern "C" { * emit the payload from psa_aead_update() instead of buffering it all for * finish(). */ #define WOLFSSL_AESGCM_STREAM +/* PSA allows 4 and 8 byte GCM tags; wolfCrypt's default minimum is 12 */ +#ifndef WOLFSSL_MIN_AUTH_TAG_SZ +#define WOLFSSL_MIN_AUTH_TAG_SZ 4 +#endif #define HAVE_AESCCM #define HAVE_AES_ECB /* Constant-time AES. WOLFSSL_AES_TOUCH_LINES keeps the table access pattern