diff --git a/.github/workflows/build-config-matrix.yml b/.github/workflows/build-config-matrix.yml index 026ddf1..702c6f8 100644 --- a/.github/workflows/build-config-matrix.yml +++ b/.github/workflows/build-config-matrix.yml @@ -92,12 +92,14 @@ jobs: 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 @@ -111,9 +113,10 @@ jobs: - name: Clone sibling wolfSSL run: git clone --depth 1 --branch ${{ matrix.wolfssl-ref }} https://github.com/wolfSSL/wolfssl ../wolfssl + # Builds the library, then runs the PSA_WANT_* surface test against it - name: Build ${{ matrix.config.name }} env: - BUILD_TARGET: ${{ matrix.config.target || 'libwolfpsa.so' }} + BUILD_TARGET: ${{ matrix.config.target || 'want-surface-test' }} # 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/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 11c06a6..c41b6d2 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,4 @@ 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 diff --git a/Makefile b/Makefile index fed5ff8..4c9c141 100644 --- a/Makefile +++ b/Makefile @@ -82,7 +82,8 @@ WOLFCRYPT_OBJ_PIC := $(patsubst $(WOLFSSL_PATH)/wolfcrypt/src/%.c,$(OBJDIR_PIC)/ 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 @@ -118,7 +119,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 all: $(LIBNAME) $(SHLIBNAME) @@ -135,6 +137,26 @@ $(LIBNAME): $(OBJ) $(WOLFCRYPT_OBJ) $(SHLIBNAME): $(OBJ_PIC) $(WOLFCRYPT_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) $(WOLFCRYPT_OBJ) $(WOLFCRYPT_OBJ_PIC): $(PSA_CONFIG) + +FORCE: + +want-surface-test: $(LIBNAME) $(SHLIBNAME) + $(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 + $(OBJDIR)/%.o: src/%.c @mkdir -p $(OBJDIR) $(CC) $(CPPFLAGS) $(DEPFLAGS) $(CFLAGS) -c $< -o $@ @@ -210,6 +232,7 @@ UNIT_TESTS := psa_api_test \ psa_import_zero_length_test \ psa_copy_key_cross_lifetime_test \ psa_zero_capacity_buffer_test \ + psa_want_surface_test \ psa_tf_psa_extra_test # Run the unit test loop from the repo root (psa_rsa_pss_interop_test reads diff --git a/README.md b/README.md index fcfeb8f..be6e887 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,12 @@ 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. + ## Testing (PSA Architecture Test Suite) See `test-psa-api/README.md` for instructions on how to run the tests. diff --git a/src/psa_key_storage.c b/src/psa_key_storage.c index 120eaba..0ee8647 100644 --- a/src/psa_key_storage.c +++ b/src/psa_key_storage.c @@ -192,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; @@ -224,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) diff --git a/src/psa_tf_psa.c b/src/psa_tf_psa.c index d2391c9..f0fcbeb 100644 --- a/src/psa_tf_psa.c +++ b/src/psa_tf_psa.c @@ -34,6 +34,11 @@ #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(); 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 254c4a5..717ea29 100644 --- a/test/Makefile +++ b/test/Makefile @@ -2,6 +2,11 @@ WOLFPSA_PATH ?= .. WOLFSSL_PATH ?= $(WOLFPSA_PATH)/../wolfssl WOLFPSA_ABS := $(abspath $(WOLFPSA_PATH)) BUILD_DIR ?= build +WOLFPSA_BUILD_DIR := $(WOLFPSA_ABS)/$(BUILD_DIR) +ifneq ($(filter /%,$(BUILD_DIR)),) +WOLFPSA_BUILD_DIR := $(BUILD_DIR) +endif +PSA_CONFIG := $(WOLFPSA_BUILD_DIR)/include/crypto_config_generated.h WOLFSSL_CANDIDATE_DIRS := \ $(WOLFSSL_PATH)/build \ @@ -56,6 +61,7 @@ ifneq ($(WOLFSSL_BUILD_DIR),) CPPFLAGS += -I$(WOLFSSL_BUILD_DIR) endif CPPFLAGS += -I$(WOLFPSA_PATH) -I$(WOLFPSA_PATH)/wolfpsa +CPPFLAGS += -I$(WOLFPSA_BUILD_DIR)/include CPPFLAGS += -I$(WOLFPSA_PATH)/src CPPFLAGS += -I$(WOLFPSA_PATH)/test/psa_server @@ -125,7 +131,8 @@ PSA_REGRESSION_TESTS = psa_aead_multipart_test \ psa_store_dir_validation_test \ psa_import_zero_length_test \ psa_copy_key_cross_lifetime_test \ - psa_zero_capacity_buffer_test + psa_zero_capacity_buffer_test \ + psa_want_surface_test BINARIES += $(PSA_REGRESSION_TESTS) # TF-PSA-Crypto extension tests, built with the default link recipe. @@ -146,8 +153,8 @@ PSA_DES3_STACK_SCRUB_TEST_OBJS = psa_server/psa_des3_stack_scrub_test.o PSA_ECC_BIT_INFERENCE_TEST_OBJS = psa_server/psa_ecc_bit_inference_test.o PSA_ECC_CURVE_ID_TEST_OBJS = psa_server/psa_ecc_curve_id_test.o PSA_RANDOM_SIZE_TEST_OBJS = psa_server/psa_random_size_test.o -PSA_ECC_CURVE_ID_TEST_DEPS = $(WOLFPSA_PATH)/$(BUILD_DIR)/obj/psa_asymmetric.o \ - $(WOLFPSA_PATH)/$(BUILD_DIR)/obj/psa_pq.o +PSA_ECC_CURVE_ID_TEST_DEPS = $(WOLFPSA_BUILD_DIR)/obj/psa_asymmetric.o \ + $(WOLFPSA_BUILD_DIR)/obj/psa_pq.o PSA_RSA_PSS_TEST_OBJS = psa_server/psa_rsa_pss_interop_test.o .PHONY: all clean rebuild-wolfssl-psa require-wolfssl-lib FORCE @@ -196,6 +203,9 @@ $(PSA_REGRESSION_TESTS) $(PSA_TF_PSA_TESTS): %: require-wolfssl-lib \ psa_server/%.o $(CC) $(CFLAGS) -o $@ psa_server/$@.o $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) +# Its expectations come from the library's PSA_WANT_* macros +psa_server/psa_want_surface_test.o: $(wildcard $(PSA_CONFIG)) + ifdef WOLFSSL_HAS_PSA_TLS psa_tls_server: psa_server/psa_tls_server.o $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(RPATH_WOLFPSA) $(RPATH_WOLFSSL) @@ -213,7 +223,7 @@ 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 + -I$(WOLFPSA_ABS)/src -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 @@ -304,7 +314,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_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 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..6cd1cf0 --- /dev/null +++ b/test/psa_server/psa_want_surface_test.c @@ -0,0 +1,330 @@ +/* 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 sha256 = IS_DEFINED(PSA_WANT_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 = sha256 && 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), + sha256 && 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), + sha256 && 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), + sha256 && 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..f15337c 100644 --- a/test/wolfcrypt-benchmark/Makefile +++ b/test/wolfcrypt-benchmark/Makefile @@ -2,6 +2,11 @@ WOLFPSA_PATH ?= ../.. WOLFSSL_PATH ?= $(WOLFPSA_PATH)/../wolfssl +BUILD_DIR ?= build +WOLFPSA_BUILD_DIR := $(WOLFPSA_PATH)/$(BUILD_DIR) +ifneq ($(filter /%,$(BUILD_DIR)),) +WOLFPSA_BUILD_DIR := $(BUILD_DIR) +endif # libwolfpsa.so location and link flags. WOLFPSA_LIBDIR ?= $(WOLFPSA_PATH) @@ -13,7 +18,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_BUILD_DIR)/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/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 078f41f..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 @@ -314,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) \ @@ -439,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 @@ -469,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/tf-psa-crypto/build_info.h b/wolfpsa/tf-psa-crypto/build_info.h index 7da0f57..f70d357 100644 --- a/wolfpsa/tf-psa-crypto/build_info.h +++ b/wolfpsa/tf-psa-crypto/build_info.h @@ -30,9 +30,7 @@ #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 */ #if defined(PSA_WANT_KEY_TYPE_AES) #define MBEDTLS_AES_C diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 37426e1..bf0f218 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. 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))