From 3f68733ae62559656696ea559e2b5ac4998771d7 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 21 Apr 2026 16:47:09 +0200 Subject: [PATCH 01/39] travis: Add OpenSSL custom build The new dependency to OpenSSL requires a custom built OpenSSL, as long it is available as distro package. This workaround can be removed, if OpenSSL v3.5 or later is available as distro package. Signed-off-by: Holger Dengler --- .travis.yml | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index b38836db..6df1abbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,29 @@ arch: s390x os: linux -dist: jammy -language: cpp +dist: noble +language: c sudo: required -env: | - CFLAGS="-O3 -Wextra -Wextra -Werror" - CXXFLAGS="-O3 -Wextra -Wextra -Werror" + compiler: - gcc - clang before_install: - sudo apt-get update -qq - - sudo apt-get install cmake libjson-c-dev + - sudo apt-get install -y cmake libjson-c-dev + +before_script: + - git clone https://github.com/openssl/openssl.git + - pushd openssl + - git checkout --track origin/openssl-3.5 + - ./config -w 2> >(tee) + - make -s -j4 2> >(tee) + - export OPENSSL_DIR=$(pwd) + - export PATH=${OPENSSL_DIR}/apps/:${PATH} + - export LD_LIBRARY_PATH=${OPENSSL_DIR}:${LD_LIBRARY_PATH} + - popd script: - set -o pipefail - - mkdir build 2> >(tee) && cd build 2> >(tee) - - cmake -DBUILD_TEST=ON .. 2> >(tee) - - make 2> >(tee) + - cmake -B build -S . -DCMAKE_INCLUDE_PATH=${OPENSSL_DIR} -DCMAKE_LIBRARY_PATH=${OPENSSL_DIR} 2> >(tee) + - cmake --build build --target zpc 2> >(tee) From 04396629e074808971d559444e0c9b5daa597fb5 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 4 May 2026 16:08:56 +0200 Subject: [PATCH 02/39] cmake: Fix broken gtest The gtest release 1.11.0 produce build problems because of outdated versions. Updating to version v1.12.1 fixes the problems. While at it, migrate from archive-download to git checkout. Signed-off-by: Holger Dengler --- CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ffb978..1b9f7ce4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,11 +146,11 @@ if (BUILD_TEST) enable_testing() -set(GTEST_URL - https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip +set(GTEST_GIT + https://github.com/google/googletest.git ) -set(GTEST_SHA256 - 353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a +set(GTEST_TAG + release-1.12.1 ) set(WYCHEPROOF_GIT @@ -355,8 +355,8 @@ ExternalProject_Add(wycheproof ) ExternalProject_Add(gtest - URL ${GTEST_URL} - URL_HASH SHA256=${GTEST_SHA256} + GIT_REPOSITORY ${GTEST_GIT} + GIT_TAG ${GTEST_TAG} PREFIX ${CMAKE_BINARY_DIR}/gtest INSTALL_COMMAND "" TEST_COMMAND "" From 1a6bb73a0f926409a7b4723e8eade880bf939179 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 10:07:36 +0200 Subject: [PATCH 03/39] cmake: Convert zpc target to object module The libzpc API is no longer exposed as static or shared library. The object module is only available for internal purpose. Signed-off-by: Holger Dengler --- CMakeLists.txt | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b9f7ce4..6cf5d255 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,17 +103,11 @@ set(ZPC_LIBS Threads::Threads ${CMAKE_DL_LIBS} ) -add_library(zpc ${ZPC_SOURCES}) - -set_target_properties(zpc - PROPERTIES - VERSION ${ZPC_VERSION_MAJOR}.${ZPC_VERSION_MINOR}.${ZPC_VERSION_PATCH} - SOVERSION ${ZPC_VERSION_MAJOR} - PUBLIC_HEADER "${ZPC_HEADERS}" - C_VISIBILITY_PRESET hidden - CXX_VISIBILITY_PRESET hidden - LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/libzpc.map" +add_library( + zpc OBJECT + ${ZPC_SOURCES} ) + target_include_directories(zpc PRIVATE include src src/zkey) target_link_libraries(zpc ${ZPC_LIBS}) target_compile_definitions( @@ -122,21 +116,8 @@ target_compile_definitions( ZPC_VERSION_MINOR=${ZPC_VERSION_MINOR} ZPC_VERSION_PATCH=${ZPC_VERSION_PATCH} ) -configure_file(libzpc.pc.in libzpc.pc @ONLY) include(GNUInstallDirs) -install( - TARGETS zpc - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/zpc/ -) - -install( - FILES ${CMAKE_BINARY_DIR}/libzpc.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig -) - ########################################################### # Test @@ -375,7 +356,7 @@ set(ZPC_TEST_LIBS ${GTEST_LIB_DIR}/libgtest.a Threads::Threads json-c::json-c - zpc + $ ) set(ZPC_TEST_SOURCES From 8089287ab2310bcbf364aa0a9d05ee3c9a5eaab8 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 11 May 2026 09:04:30 +0200 Subject: [PATCH 04/39] cmake: Introduce build option BUILD_INTERNAL_TEST As the libzpc API is no longer externally available, also the extensive testing (gtest/wycheproof) has to be made internal. Introduce a new build option BUILD_INTERNAL_TEST. Enabling this new option will build the extensive tests. Signed-off-by: Holger Dengler --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cf5d255..46706b1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,8 +122,9 @@ include(GNUInstallDirs) # Test option(BUILD_TEST OFF) +option(BUILD_INTERNAL_TEST OFF) -if (BUILD_TEST) +if (BUILD_INTERNAL_TEST) enable_testing() @@ -404,7 +405,7 @@ target_include_directories(runtest PRIVATE include src ${GTEST_INCLUDE_DIR}) include(GoogleTest) gtest_discover_tests(runtest) -endif () +endif () # BUILD_INTERNAL_TEST ########################################################### # doc From 6299d32097a9fbf047b6a668026435df5de69cf9 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 07:35:20 +0200 Subject: [PATCH 05/39] cmake: Harmonize indent Adjust indention, no functional change. Signed-off-by: Holger Dengler --- CMakeLists.txt | 70 +++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46706b1b..7b902bc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,7 @@ add_definitions( ) set(ZPC_LIBS - Threads::Threads ${CMAKE_DL_LIBS} + Threads::Threads ${CMAKE_DL_LIBS} ) add_library( @@ -207,19 +207,19 @@ set(NIST_AES_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_aes_ecb.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_ecb.pl - ${NIST_AES_SOURCE_DIR}/ECBMMT* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes + OUTPUT nist_aes_ecb.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_ecb.pl + ${NIST_AES_SOURCE_DIR}/ECBMMT* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes ) add_custom_target(nist_aes_ecb_json ALL DEPENDS nist_aes_ecb.json) add_custom_command( - OUTPUT nist_aes_cbc.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_cbc.pl - ${NIST_AES_SOURCE_DIR}/CBCMMT* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes + OUTPUT nist_aes_cbc.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_cbc.pl + ${NIST_AES_SOURCE_DIR}/CBCMMT* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes ) add_custom_target(nist_aes_cbc_json ALL DEPENDS nist_aes_cbc.json) @@ -237,11 +237,11 @@ set(NIST_AES_XTS_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_aes_xts.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_xts.pl - ${NIST_AES_XTS_SOURCE_DIR}/'format tweak value input - 128 hex str'/* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes_xts + OUTPUT nist_aes_xts.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_xts.pl + ${NIST_AES_XTS_SOURCE_DIR}/'format tweak value input - 128 hex str'/* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes_xts ) add_custom_target(nist_aes_xts_json ALL DEPENDS nist_aes_xts.json) @@ -259,11 +259,11 @@ set(NIST_AES_GCM_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_aes_gcm.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_gcm.pl - ${NIST_AES_GCM_SOURCE_DIR}/* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes_gcm + OUTPUT nist_aes_gcm.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_gcm.pl + ${NIST_AES_GCM_SOURCE_DIR}/* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes_gcm ) add_custom_target(nist_aes_gcm_json ALL DEPENDS nist_aes_gcm.json) @@ -309,20 +309,20 @@ set(NIST_ECDSA_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_ecdsa.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_ecdsa.pl - ${NIST_ECDSA_SOURCE_DIR}/SigGenComponent.txt - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_ecdsa + OUTPUT nist_ecdsa.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_ecdsa.pl + ${NIST_ECDSA_SOURCE_DIR}/SigGenComponent.txt + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_ecdsa ) add_custom_target(nist_ecdsa_json ALL DEPENDS nist_ecdsa.json) add_custom_command( - OUTPUT nist_eddsa.json - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_SOURCE_DIR}/misc/nist_eddsa.json - ${CMAKE_BINARY_DIR} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + OUTPUT nist_eddsa.json + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/misc/nist_eddsa.json + ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) add_custom_target(nist_eddsa_json ALL DEPENDS nist_eddsa.json) @@ -339,10 +339,10 @@ ExternalProject_Add(wycheproof ExternalProject_Add(gtest GIT_REPOSITORY ${GTEST_GIT} GIT_TAG ${GTEST_TAG} - PREFIX ${CMAKE_BINARY_DIR}/gtest - INSTALL_COMMAND "" - TEST_COMMAND "" - CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" + PREFIX ${CMAKE_BINARY_DIR}/gtest + INSTALL_COMMAND "" + TEST_COMMAND "" + CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" ) ExternalProject_Get_Property(gtest SOURCE_DIR BINARY_DIR) set(GTEST_INCLUDE_DIR From 9910b8238ff9e44b696cb6df8c6716fb31970196 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 20:11:16 +0200 Subject: [PATCH 06/39] cmake: Add OpenSSL package The zpc functionality will be exposed via the OpenSSL API. Query the required OpenSSL package during build. Signed-off-by: Holger Dengler --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b902bc7..5f2b2484 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,11 @@ find_package(json-c REQUIRED ) +find_package(OpenSSL + 3.5.0 + REQUIRED +) + add_definitions( -D_GNU_SOURCE ) From 67f148f8d7017fa4b6e6a0f2673220ecc5f9b2fa Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 16:42:12 +0200 Subject: [PATCH 07/39] provider: Add base provider The provider is the base to plug-in further implementation like key-management, ciphers and so on. It has no functionality itself. Signed-off-by: Holger Dengler --- src/ossl.h | 22 +++ src/provider.c | 353 +++++++++++++++++++++++++++++++++++++++++++++++++ src/provider.h | 66 +++++++++ 3 files changed, 441 insertions(+) create mode 100644 src/ossl.h create mode 100644 src/provider.c create mode 100644 src/provider.h diff --git a/src/ossl.h b/src/ossl.h new file mode 100644 index 00000000..c671af14 --- /dev/null +++ b/src/ossl.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _OSSL_H +#define _OSSL_H + +#include + +#define OSSL_RV_TRUE (1) +#define OSSL_RV_FALSE (0) +#define OSSL_RV_OK (1) +#define OSSL_RV_ERR (0) + +#define ALGORITHM_DEFN(name, prop, fn, desc) { name, prop, fn, desc } +#define ALGORITHM_END { NULL, NULL, NULL, NULL } + +#define DISPATCH_DEFN(MODULE, NAME, name) { OSSL_FUNC_##MODULE##_##NAME, (void (*)(void))name } +#define DISPATCH_END { 0, NULL } + +#define DECL_DISPATCH_FUNC(MODULE, NAME, name) \ + static OSSL_FUNC_##MODULE##_##NAME##_fn name + +#endif /* _OSSL_H */ diff --git a/src/provider.c b/src/provider.c new file mode 100644 index 00000000..7ef3f669 --- /dev/null +++ b/src/provider.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include + +#include + +#include "ossl.h" +#include "provider.h" + +#define C(str) (void *)(str) +static const OSSL_ITEM reason_strings[] = { + { ZPC_ERROR_ARG1NULL, + C("argument 1 NULL") }, + { ZPC_ERROR_ARG2NULL, + C("argument 2 NULL") }, + { ZPC_ERROR_ARG3NULL, + C("argument 3 NULL") }, + { ZPC_ERROR_ARG4NULL, + C("argument 4 NULL") }, + { ZPC_ERROR_ARG5NULL, + C("argument 5 NULL") }, + { ZPC_ERROR_ARG6NULL, + C("argument 6 NULL") }, + { ZPC_ERROR_ARG7NULL, + C("argument 7 NULL") }, + { ZPC_ERROR_ARG8NULL, + C("argument 8 NULL") }, + { ZPC_ERROR_ARG1RANGE, + C("argument 1 out of range") }, + { ZPC_ERROR_ARG2RANGE, + C("argument 2 out of range") }, + { ZPC_ERROR_ARG3RANGE, + C("argument 3 out of range") }, + { ZPC_ERROR_ARG4RANGE, + C("argument 4 out of range") }, + { ZPC_ERROR_ARG5RANGE, + C("argument 5 out of range") }, + { ZPC_ERROR_ARG6RANGE, + C("argument 6 out of range") }, + { ZPC_ERROR_ARG7RANGE, + C("argument 7 out of range") }, + { ZPC_ERROR_ARG8RANGE, + C("argument 8 out of range") }, + { ZPC_ERROR_MALLOC, + C("malloc failed") }, + { ZPC_ERROR_KEYNOTSET, + C("no key is set") }, + { ZPC_ERROR_KEYSIZE, + C("invalid key size") }, + { ZPC_ERROR_IVNOTSET, + C("IV not set") }, + { ZPC_ERROR_IVSIZE, + C("invalid IV size") }, + { ZPC_ERROR_TAGSIZE, + C("invalid tag size") }, + { ZPC_ERROR_TAGMISMATCH, + C("tag mismatch") }, + { ZPC_ERROR_HWCAPS, + C("function not supported") }, + { ZPC_ERROR_SMALLOUTBUF, + C("output buffer too small") }, + { ZPC_ERROR_APQNSNOTSET, + C("APQNs not set") }, + { ZPC_ERROR_KEYTYPE, + C("invalid key type") }, + { ZPC_ERROR_KEYTYPENOTSET, + C("key type not set") }, + { ZPC_ERROR_IOCTLGENSECK2, + C("PKEY_GENSECK2 ioctl failed") }, + { ZPC_ERROR_IOCTLCLR2SECK2, + C("PKEY_CLR2SECK2 ioctl failed") }, + { ZPC_ERROR_IOCTLBLOB2PROTK2, + C("PKEY_BLOB2PROTK2 ioctl failed") }, + { ZPC_ERROR_WKVPMISMATCH, + C("wrapping key verification pattern mismatch") }, + { ZPC_ERROR_DEVPKEY, + C("opening /dev/pkey failed") }, + { ZPC_ERROR_CLEN, + C("ciphertext too long") }, + { ZPC_ERROR_MLEN, + C("message too long") }, + { ZPC_ERROR_AADLEN, + C("additional authenticated data too long") }, + { ZPC_ERROR_PARSE, + C("parse error") }, + { ZPC_ERROR_APQNNOTFOUND, + C("APQN not found in APQN list") }, + { ZPC_ERROR_MKVPLEN, + C("MKVP too long") }, + { ZPC_ERROR_INITLOCK, + C("initializing a lock failed") }, + { ZPC_ERROR_OBJINUSE, + C("object is in use") }, + { ZPC_ERROR_IOCTLAPQNS4KT, + C("PKEY_APQNS4KT ioctl failed") }, + { ZPC_ERROR_KEYSIZENOTSET, + C("key-size not set") }, + { ZPC_ERROR_IOCTLGENPROTK, + C("PKEY_GENPROTK ioctl failed") }, + { ZPC_ERROR_PROTKEYONLY, + C("protected-key only") }, + { ZPC_ERROR_KEYSEQUAL, + C("keys are equal") }, + { ZPC_ERROR_NOTSUP, + C("not supported") }, + { ZPC_ERROR_EC_INVALID_CURVE, + C("Invalid EC curve") }, + { ZPC_ERROR_EC_CURVE_NOTSET, + C("EC curve not set") }, + { ZPC_ERROR_EC_PRIVKEY_NOTSET, + C("EC private key not set") }, + { ZPC_ERROR_EC_PUBKEY_NOTSET, + C("EC public key not set") }, + { ZPC_ERROR_EC_NO_KEY_PARTS, + C("No EC key parts given") }, + { ZPC_ERROR_EC_SIGNATURE_INVALID, + C("signature invalid") }, + { ZPC_ERROR_IOCTLBLOB2PROTK3, + C("PKEY_BLOB2PROTK3 ioctl failed") }, + { ZPC_ERROR_IOCTLCLR2SECK3, + C("PKEY_CLR2SECK3 ioctl failed") }, + { ZPC_ERROR_APQNS_NOTSET, + C("No APQNs set for this key, but required for this operation") }, + { ZPC_ERROR_EC_SIGNATURE_LENGTH, + C("Signature length is invalid for this EC key") }, + { ZPC_ERROR_EC_KEY_PARTS_INCONSISTENT, + C("Given public/private key parts are inconsistent") }, + { ZPC_ERROR_CCA_HOST_LIB_NOT_AVAILABLE, + C("CCA host library not available, but required for this operation") }, + { ZPC_ERROR_EP11_HOST_LIB_NOT_AVAILABLE, + C("EP11 host library not available, but required for this operation") }, + { ZPC_ERROR_EC_PUBKEY_LENGTH, + C("The given EC public key length is invalid") }, + { ZPC_ERROR_EC_PRIVKEY_LENGTH, + C("The given EC private key length is invalid") }, + { ZPC_ERROR_EC_NO_CCA_SECUREKEY_TOKEN, + C("The given buffer does not contain a valid CCA secure key token") }, + { ZPC_ERROR_EC_NO_EP11_SECUREKEY_TOKEN, + C("The given buffer does not contain a valid EP11 secure key token") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_LENGTH, + C("The imported buffer contains an EP11 SPKI with an invalid length") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_CURVE, + C("The imported buffer contains an EP11 SPKI with an invalid EC curve") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_PUBKEY, + C("The imported buffer contains an EP11 SPKI with an invalid public key") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_MKVP, + C("The imported buffer contains an EP11 MACed SPKI with an invalid MKVP") }, + { ZPC_ERROR_BLOB_NOT_PKEY_EXTRACTABLE, + C("The imported buffer contains a key blob that cannot be transformed into a protected key.") }, + { ZPC_ERROR_APQNS_INVALID_VERSION, + C("At least one APQN version is invalid for this function.") }, + { ZPC_ERROR_AES_NO_EP11_SECUREKEY_TOKEN, + C("The given buffer does not contain a valid EP11 AES secure key token.") }, + { ZPC_ERROR_AES_NO_CCA_DATAKEY_TOKEN, + C("The given buffer does not contain a valid CCA datakey token") }, + { ZPC_ERROR_AES_NO_CCA_CIPHERKEY_TOKEN, + C("The given buffer does not contain a valid CCA cipherkey token") }, + { ZPC_ERROR_RNDGEN, + C("Error creating random bytes") }, + { ZPC_ERROR_GCM_IV_CREATED_INTERNALLY, + C("Invalid usage of a gcm context with an internally created iv") }, + { ZPC_ERROR_UV_PVSECRETS_NOT_AVAILABLE, + C("Support for UV retrievable secrets is not available, but required for this function.") }, + { ZPC_ERROR_PVSECRET_TYPE_NOT_SUPPORTED, + C("The given pvsecret type is not supported by libzpc.") }, + { ZPC_ERROR_PVSECRET_ID_NOT_FOUND_IN_UV_OR_INVALID_TYPE, + C("The given pvsecret ID does either not exist or belongs to a different secret type.") }, + { ZPC_ERROR_IOCTLVERIFYKEY2, + C("PKEY_VERIFYKEY2 ioctl failed.") }, + { ZPC_ERROR_HMAC_HASH_FUNCTION_NOTSET, + C("HMAC hash function not set.") }, + { ZPC_ERROR_HMAC_HASH_FUNCTION_INVALID, + C("HMAC hash function invalid.") }, + { ZPC_ERROR_HMAC_KEYGEN_VIA_SYSFS, + C("HMAC key generation via sysfs attributes failed.") }, + { ZPC_ERROR_CREATE_BLOCKSIZED_KEY, + C("Creating a block-sized HMAC key failed.") }, + { ZPC_ERROR_XTS_KEYGEN_VIA_SYSFS, + C("Creating a full-xts key via sysfs attributes failed") }, + { 0, NULL }, +}; +#undef C + +static const OSSL_PARAM prov_param_types[] = { + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_VERSION, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_BUILDINFO, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_STATUS, OSSL_PARAM_INTEGER, NULL, 0), + OSSL_PARAM_END, +}; + +static int prov_ctx_init(struct provider_ctx *pctx, const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in) +{ + const OSSL_DISPATCH *iter_in; + OSSL_LIB_CTX *libctx; + + if (!pctx) + return OSSL_RV_ERR; + + libctx = OSSL_LIB_CTX_new_from_dispatch(handle, in); + if (!libctx) + return OSSL_RV_ERR; + + pctx->libctx = libctx; + pctx->handle = handle; + pctx->state = PROVIDER_INITIALIZED; + + for (iter_in = in; iter_in->function_id != 0; iter_in++) { + switch (iter_in->function_id) { + case OSSL_FUNC_CORE_NEW_ERROR: + pctx->core_new_error = OSSL_FUNC_core_new_error(iter_in); + break; + case OSSL_FUNC_CORE_SET_ERROR_DEBUG: + pctx->core_set_error_debug = OSSL_FUNC_core_set_error_debug(iter_in); + break; + case OSSL_FUNC_CORE_VSET_ERROR: + pctx->core_vset_error = OSSL_FUNC_core_vset_error(iter_in); + break; + default: + continue; + } + } + return OSSL_RV_OK; +} + +static void prov_teardown(void *vpctx) +{ + OPENSSL_free(vpctx); +} + +static const OSSL_PARAM *prov_gettable_params(void *vpctx __unused) +{ + return prov_param_types; +} + +static int prov_get_params(void *vpctx, OSSL_PARAM params[]) +{ + struct provider_ctx *pctx = vpctx; + OSSL_PARAM *p; + + if (!pctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_NAME); + if (p && (OSSL_PARAM_set_utf8_ptr(p, PROV_NAME) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_VERSION); + if (p && (OSSL_PARAM_set_utf8_ptr(p, PROV_VERSION) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_BUILDINFO); + if (p && (OSSL_PARAM_set_utf8_ptr(p, PROV_VERSION) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_STATUS); + if (p && (OSSL_PARAM_set_int(p, pctx->state) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + return OSSL_RV_OK; +} + +static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, int *no_cache) +{ + struct provider_ctx *pctx = vpctx; + const OSSL_ALGORITHM *ops; + + if (!pctx || pctx->state == PROVIDER_UNINITIALIZED) + return NULL; + + switch (operation_id) { + default: + ops = NULL; + goto out; + } + + if (no_cache) + *no_cache = OSSL_RV_FALSE; +out: + return ops; +} + +static const OSSL_ITEM *prov_get_reason_strings(void *vpctx __unused) +{ + return reason_strings; +} + +static const OSSL_DISPATCH provider_dispatch_table[] = { +#define FUNC(func) (void (*)(void))(func) + { OSSL_FUNC_PROVIDER_TEARDOWN, FUNC(prov_teardown) }, + { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, FUNC(prov_gettable_params) }, + { OSSL_FUNC_PROVIDER_GET_PARAMS, FUNC(prov_get_params) }, + { OSSL_FUNC_PROVIDER_QUERY_OPERATION, FUNC(prov_query_operation) }, + { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, FUNC(prov_get_reason_strings) }, + { 0, NULL } +#undef FUNC +}; + +static int prov_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, + void **vpctx) +{ + struct provider_ctx *pctx; + int rv = OSSL_RV_ERR; + + if (!handle || !in || !out || !vpctx) + return OSSL_RV_ERR; + + pctx = OPENSSL_zalloc(sizeof(*pctx)); + if (!pctx) + return OSSL_RV_ERR; + + if (!prov_ctx_init(pctx, handle, in)) + goto err; + + *vpctx = pctx; + *out = provider_dispatch_table; + return OSSL_RV_OK; + +err: + OPENSSL_free(pctx); + return rv; +} + +void prov_err_raise(struct provider_ctx *pctx, const char *file, int line, + const char *func, int reason, const char *fmt, ...) +{ + va_list args; + + if (!pctx || !pctx->core_new_error || + !pctx->core_set_error_debug || !pctx->core_vset_error) + return ERR_raise(ERR_LIB_PROV, reason); + + va_start(args, fmt); + pctx->core_new_error(pctx->handle); + pctx->core_set_error_debug(pctx->handle, file, line, func); + pctx->core_vset_error(pctx->handle, reason, fmt, args); + va_end(args); +} + +int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, + void **provctx) +{ + return prov_init(handle, in, out, provctx); +} diff --git a/src/provider.h b/src/provider.h new file mode 100644 index 00000000..2ab36556 --- /dev/null +++ b/src/provider.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _PROVIDER_H +#define _PROVIDER_H + +#include +#include +#include + +#define PROV_NAME "hbkzpc" +#define PROV_VERSION "2.0.0" +#define PROV_PROP "provider="PROV_NAME +#define PROV_PROP_FWD "provider!="PROV_NAME + +#define PROV_NAME_EC "EC" +#define PROV_NAMES_EC "EC:id-ecPublicKey:1.2.840.10045.2.1" +#define PROV_DESC_EC "hbkzpc EC implementation" + +#define PROV_NAME_ECDSA "ECDSA" +#define PROV_NAMES_ECDSA PROV_NAME_ECDSA +#define PROV_DESC_ECDSA "hbkzpc ECDSA Implementation" + +#define PROV_NAME_ED25519 "ED25519" +#define PROV_NAMES_ED25519 "ED25519:1.3.101.112" +#define PROV_DESC_ED25519 "hbkzpc ED25519 Implementation" + +#define PROV_NAME_ED448 "ED448" +#define PROV_NAMES_ED448 "ED448:1.3.101.113" +#define PROV_DESC_ED448 "hbkzpc ED448 Implementation" + +#define __unused __attribute__((unused)) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +enum provider_state { + PROVIDER_UNINITIALIZED = 0, + PROVIDER_INITIALIZED, +}; + +struct provider_ctx { + const OSSL_CORE_HANDLE *handle; + OSSL_LIB_CTX *libctx; + + enum provider_state state; + + OSSL_FUNC_core_new_error_fn *core_new_error; + OSSL_FUNC_core_set_error_debug_fn *core_set_error_debug; + OSSL_FUNC_core_vset_error_fn *core_vset_error; +}; + +void prov_err_raise(struct provider_ctx *pctx, const char *file, int line, + const char *func, int reason, const char *fmt, ...); +#define PROV_ERR_raise(pctx, reason) \ + prov_err_raise(pctx, OPENSSL_FILE, OPENSSL_LINE, OPENSSL_FUNC, reason, NULL) + +#endif /* _PROVIDER_H */ From b6e3fee7b2b8e0b8c539ef199339aa471a6e1789 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 20:11:48 +0200 Subject: [PATCH 08/39] cmake: Add zpcprovider build target Add a module build target for teh zpcprovider. Other than shared objects, the provider module has no so-name and also no API versioning. Signed-off-by: Holger Dengler --- CMakeLists.txt | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ zpcprovider.map | 6 ++++++ 2 files changed, 59 insertions(+) create mode 100644 zpcprovider.map diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f2b2484..794a37ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,59 @@ target_compile_definitions( ) include(GNUInstallDirs) +########################################################### +# zpcprovider + +set(ZPCPROVIDER_SOURCES + src/provider.c +) + +add_library( + zpcprovider MODULE + ${ZPCPROVIDER_SOURCES} +) + +set_target_properties( + zpcprovider + PROPERTIES + PREFIX "" +) + +target_include_directories( + zpcprovider + PRIVATE + src + include + ${OPENSSL_INCLUDE_DIR} +) + +target_link_libraries( + zpcprovider + PRIVATE + OpenSSL::Crypto +) + +target_link_options( + zpcprovider + PRIVATE + -Wl,--version-script=${CMAKE_SOURCE_DIR}/zpcprovider.map +) + +install( + TARGETS zpcprovider + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBEXECDIR} +) + +# install( +# FILES ${CMAKE_SOURCE_DIR}/man/zpcprovider.cnf.5 +# DESTINATION ${CMAKE_INSTALL_MANDIR}/man5 +# ) +# +# install( +# FILES ${CMAKE_SOURCE_DIR}/man/zpcprovider.7 +# DESTINATION ${CMAKE_INSTALL_MANDIR}/man7 +# ) + ########################################################### # Test diff --git a/zpcprovider.map b/zpcprovider.map new file mode 100644 index 00000000..783f9fc9 --- /dev/null +++ b/zpcprovider.map @@ -0,0 +1,6 @@ +{ +global: + OSSL_provider_init; +local: + *; +}; From 0a5c1add0fd77d74f4a80266f65d30d8c9dc4e96 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 14:07:00 +0100 Subject: [PATCH 09/39] provider: Add provider-specific key object The provider-specific key object structure is shared between the provider components and references to the internal zpc-key structure(s). Signed-off-by: Holger Dengler --- src/object.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/object.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/object.c create mode 100644 src/object.h diff --git a/src/object.c b/src/object.c new file mode 100644 index 00000000..89349683 --- /dev/null +++ b/src/object.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include + +#include "object.h" + +static void _obj_free(struct obj *obj) +{ + OPENSSL_free(obj->origin_type); + OPENSSL_free(obj->origin_alg); + OPENSSL_free(obj->origin_blob.p); + OPENSSL_free(obj->origin_pubkey.p); + OPENSSL_free(obj->apqns); + OPENSSL_free(obj->mkvp); + + OPENSSL_free(obj->id); + OPENSSL_free(obj); +} + +void obj_free(struct obj *obj) +{ + if (!obj) + return; + + if (__atomic_sub_fetch(&obj->refcnt, 1, __ATOMIC_SEQ_CST)) + return; + + _obj_free(obj); +} + +struct obj *obj_get(struct obj *obj) +{ + if (!obj) + return NULL; + + __atomic_fetch_add(&obj->refcnt, 1, __ATOMIC_SEQ_CST); + return obj; +} + +struct obj *obj_new(struct provider_ctx *pctx) +{ + struct obj *obj; + + obj = OPENSSL_zalloc(sizeof(struct obj)); + if (!obj) + return NULL; + + obj->pctx = pctx; + + return obj_get(obj); +} diff --git a/src/object.h b/src/object.h new file mode 100644 index 00000000..06a738e4 --- /dev/null +++ b/src/object.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _OBJECT_H +#define _OBJECT_H + +#include + +#include "provider.h" + +struct data { + size_t plen; + unsigned char *p; +}; + +struct obj { + /* common */ + unsigned int refcnt; + struct provider_ctx *pctx; + + /* prototype - will be replaced by origin_blob */ + char *id; + + /* zpc keys */ + + /* origin path attrs */ + char *origin_type; + char *origin_alg; + struct data origin_blob; + struct data origin_pubkey; + + /* origin qeuery attrs */ + char *apqns; + char *mkvp; + + bool public_only; +}; + +struct obj *obj_new(struct provider_ctx *pctx); +struct obj *obj_get(struct obj *obj); +void obj_free(struct obj *obj); + +#endif /* _OBJECT_H */ From daab8319fb28ea6ffb8fbfcaff75938c3538f4ee Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 14:36:47 +0100 Subject: [PATCH 10/39] cmake: Integrate provider-specific key object Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 794a37ee..990648d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,7 @@ include(GNUInstallDirs) set(ZPCPROVIDER_SOURCES src/provider.c + src/object.c ) add_library( From a3c13f06a4f1b111b7684edb0474603c1b8af0a5 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:53:09 +0100 Subject: [PATCH 11/39] provider: Add hbkzpc-URI parser A hbkzpc-URI references a hardware-backed key origin. The parser destructs the URI into key-value pairs. Signed-off-by: Holger Dengler --- src/uri.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/uri.h | 36 +++++++++ 2 files changed, 258 insertions(+) create mode 100644 src/uri.c create mode 100644 src/uri.h diff --git a/src/uri.c b/src/uri.c new file mode 100644 index 00000000..9402e6b6 --- /dev/null +++ b/src/uri.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include + +#include "uri.h" + +#define SEP_PROTOCOL ":" +#define SEP_PATHQUERY "?" +#define SEP_KEYVALUE "=" +#define SEP_PATHATTRS ";" +#define SEP_QUERYATTRS "&" + +#define URI_PROTOCOL URI_PROTOCOL_PREFIX SEP_PROTOCOL + +#define URI_P_ORIGIN_TYPE "origin-type" SEP_KEYVALUE +#define URI_P_ORIGIN_ALG "origin-alg" SEP_KEYVALUE +#define URI_P_ORIGIN_BLOB "origin-blob" SEP_KEYVALUE +#define URI_P_ORIGIN_PUBKEY "origin-pubkey" SEP_KEYVALUE +#define URI_P_COMMENT "comment" SEP_KEYVALUE + +#define URI_Q_MKVP "mkvp" SEP_KEYVALUE +#define URI_Q_APQNS "apqns" SEP_KEYVALUE + +static void decode_pct(char *s) +{ + char *rp, *wp, *endptr; + unsigned long tmp; + char hex[3] = {0}; + size_t wp_len = 0; + + if (!s) + return; + + if (!strchr(s, '%')) + return; + + rp = wp = s; + while(*rp) { + switch(*rp) { + case '%': + if (strlen(rp) < 3) + goto out; /* invalid format */ + + rp++; /* skip % */ + memcpy(hex, rp, 2); /* 2 chars only */ + + tmp = strtoul(hex, &endptr, 16);/* convert */ + if (*endptr != '\0') + goto out; /* non-hex chars */ + *wp = (char)(tmp & 0xff); + + rp++; + break; + default: + *wp = *rp; + } + + rp++; + wp++; + wp_len++; + } +out: + *wp = '\0'; +} + +static int parse_attr(char *str, struct attr *attr) +{ + char *key, **p; + int rc = 1; + + if (!str || !attr) + goto out; + + /* skip already parsed attribute */ + if (attr->value) + goto out; + + p = &str; + key = strsep(p, SEP_KEYVALUE); + if (!p) + goto out; + + decode_pct(*p); + attr->key = key; + attr->value = *p; + + rc = 0; +out: + return rc; +} + +static inline int match_elem_attrkey(const char *elem, const char *attrkey) +{ + return (strncmp(elem, attrkey, strlen(attrkey)) == 0); +} + +static int parse_query(char *qattr, struct parsed_uri *puri) +{ + char **next; + + /* query attributes are optional */ + if (!qattr || !strlen(qattr)) + return 0; + + next = &qattr; + do { + char *e = strsep(next, SEP_QUERYATTRS); + int rc = 0; + + if (match_elem_attrkey(e, URI_Q_MKVP)) + rc = parse_attr(e, &puri->mkvp); + else if (match_elem_attrkey(e, URI_Q_APQNS)) + rc = parse_attr(e, &puri->apqns); + else + rc = 1; /* unknown attribute */ + if (rc) + return rc; + } while (*next); + + return 0; +} + +static int parse_path(char *pattr, struct parsed_uri *puri) +{ + char **next; + + /* path attributes are mandatory */ + if (!pattr || !strlen(pattr)) + return 1; + + next = &pattr; + do { + char *e = strsep(next, SEP_PATHATTRS); + int rc = 0; + + if (match_elem_attrkey(e, URI_P_ORIGIN_TYPE)) + rc = parse_attr(e, &puri->origin_type); + else if (match_elem_attrkey(e, URI_P_ORIGIN_ALG)) + rc = parse_attr(e, &puri->origin_alg); + else if (match_elem_attrkey(e, URI_P_ORIGIN_BLOB)) + rc = parse_attr(e, &puri->origin_blob); + else if (match_elem_attrkey(e, URI_P_ORIGIN_PUBKEY)) + rc = parse_attr(e, &puri->origin_pubkey); + else if (match_elem_attrkey(e, URI_P_COMMENT)) + rc = parse_attr(e, &puri->comment); + else + rc = 1; /* unknown attribute */ + if (rc) + return rc; + } while (*next); + + return 0; +} + +static int parse(char *uri, struct parsed_uri *puri) +{ + char *pattr, *qattr; + char **next; + int rc; + + if (!uri || !puri) + return 1; + + if (strncmp(uri, URI_PROTOCOL, strlen(URI_PROTOCOL)) != 0) + return 1; + + next = &uri; + + /* drop protocol */ + strsep(next, SEP_PROTOCOL); + + pattr = strsep(next, SEP_PATHQUERY); + qattr = *next; + + rc = parse_path(pattr, puri); + if (rc) { + return rc; + } + + rc = parse_query(qattr, puri); + if (rc) { + return rc; + } + + return 0; +} + +void parsed_uri_free(struct parsed_uri *puri) +{ + if (!puri) + return; + + if (puri->priv) + OPENSSL_clear_free(puri->priv, + puri->privlen); + + OPENSSL_free(puri); +} + +struct parsed_uri *parsed_uri_new(const char *uri) +{ + struct parsed_uri *puri; + + puri = OPENSSL_zalloc(sizeof(struct parsed_uri)); + if (!puri) + return NULL; + + puri->priv = OPENSSL_strdup(uri); + if (!puri->priv) + goto err; + puri->privlen = strlen(uri); + + if(parse(puri->priv, puri)) + goto err; + + return puri; + +err: + parsed_uri_free(puri); + return NULL; +} diff --git a/src/uri.h b/src/uri.h new file mode 100644 index 00000000..79cb7c14 --- /dev/null +++ b/src/uri.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _URI_H +#define _URI_H + +#include + +#define URI_PROTOCOL_PREFIX "hbkzpc" + +struct attr { + const char *key; + const char *value; + //unsigned char *data; + //size_t datalen; +}; + +struct parsed_uri { + char *priv; + size_t privlen; + + /* path attributes */ + struct attr origin_type; + struct attr origin_alg; + struct attr origin_blob; + struct attr origin_pubkey; + struct attr comment; + + /* query attributes */ + struct attr mkvp; + struct attr apqns; +}; + +struct parsed_uri *parsed_uri_new(const char *uri); +void parsed_uri_free(struct parsed_uri *puri); + +#endif /* _URI_H */ From a9dc2517905305eb664595f9ed6b185dec630a51 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:54:30 +0100 Subject: [PATCH 12/39] cmake: Add uri build target Add internal object build target for uri. The internal object can be shared between targets. Signed-off-by: Holger Dengler --- CMakeLists.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 990648d6..9d466687 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,30 @@ target_compile_definitions( ) include(GNUInstallDirs) +########################################################### +# URI + +set (URI_SOURCES + src/uri.c +) + +add_library( + uri OBJECT + ${URI_SOURCES} +) + +set_target_properties( + uri + PROPERTIES + POSITION_INDEPENDENT_CODE ON +) + +target_include_directories( + uri + PRIVATE + ${OPENSSL_INCLUDE_DIR} +) + ########################################################### # zpcprovider @@ -154,6 +178,7 @@ target_link_libraries( zpcprovider PRIVATE OpenSSL::Crypto + $ ) target_link_options( From 6d672cdaaf61a7f8144ec1124d3965910fa38406 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 03:21:27 -0500 Subject: [PATCH 13/39] provider: Add mapping helpers The mapping helpers provide mappings between e.g. algorithm strings and algorithm-related values. Signed-off-by: Holger Dengler --- src/map.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/map.h | 20 ++++++++ 2 files changed, 162 insertions(+) create mode 100644 src/map.c create mode 100644 src/map.h diff --git a/src/map.c b/src/map.c new file mode 100644 index 00000000..38334c1e --- /dev/null +++ b/src/map.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include + +#include "provider.h" +#include "map.h" + +static struct { + const char *alg; + union { + int key_size; + zpc_ec_curve_t key_curve; + }; + int object_type; + char *data_type; +} alg_map[] = { + { + .alg = SN_X9_62_prime256v1, + .key_curve = ZPC_EC_CURVE_P256, + .object_type = OSSL_OBJECT_PKEY, + .data_type = PROV_NAME_EC, + }, { + .alg = SN_secp384r1, + .key_curve = ZPC_EC_CURVE_P384, + .object_type = OSSL_OBJECT_PKEY, + .data_type = PROV_NAME_EC, + }, { + .alg = SN_secp521r1, + .key_curve = ZPC_EC_CURVE_P521, + .object_type = OSSL_OBJECT_PKEY, + .data_type = PROV_NAME_EC, + }, { + .alg = SN_ED25519, + .key_curve = ZPC_EC_CURVE_ED25519, + .object_type = OSSL_OBJECT_PKEY, + .data_type = PROV_NAME_ED25519, + }, { + .alg = SN_ED448, + .key_curve = ZPC_EC_CURVE_ED448, + .object_type = OSSL_OBJECT_PKEY, + .data_type = PROV_NAME_ED448, + }, { 0 }, +}; + +char *alg2data_type(const char *alg) +{ + char *rv = NULL; + size_t i; + + if (!alg) + return rv; + + for (i = 0; alg_map[i].alg; i++) { + if (OPENSSL_strcasecmp(alg_map[i].alg, alg) == 0) { + rv = alg_map[i].data_type; + break; + } + } + + return rv; +} + +int alg2object_type(const char *alg) +{ + int rv = OSSL_OBJECT_UNKNOWN; + size_t i; + + if (!alg) + return rv; + + for (i = 0; alg_map[i].alg; i++) { + if (OPENSSL_strcasecmp(alg_map[i].alg, alg) == 0) { + rv = alg_map[i].object_type; + break; + } + } + + return rv; +} + +zpc_ec_curve_t alg2key_curve(const char *alg) +{ + zpc_ec_curve_t rv = ZPC_EC_CURVE_INVALID; + size_t i; + + if (!alg) + return rv; + + for (i = 0; alg_map[i].alg; i++) { + if (OPENSSL_strcasecmp(alg_map[i].alg, alg) == 0) { + rv = alg_map[i].key_curve; + break; + } + } + + return rv; +} + +int alg2key_size(const char *alg) +{ + int rv = 0; + size_t i; + + if (!alg) + return rv; + + for (i = 0; alg_map[i].alg; i++) { + if (OPENSSL_strcasecmp(alg_map[i].alg, alg) == 0) { + rv = alg_map[i].key_size; + break; + } + } + + return rv; +} + +char *obj_data_type(const struct obj *obj) +{ + const char *alg = obj ? obj->origin_alg : NULL; + return alg2data_type(alg); +} + +int obj_object_type(const struct obj *obj) +{ + const char *alg = obj ? obj->origin_alg : NULL; + return alg2object_type(alg); +} + +zpc_ec_curve_t obj_key_curve(const struct obj *obj) +{ + const char *alg = obj ? obj->origin_alg : NULL; + return alg2key_curve(alg); +} + +int obj_key_size(const struct obj *obj) +{ + const char *alg = obj ? obj->origin_alg : NULL; + return alg2key_size(alg); +} diff --git a/src/map.h b/src/map.h new file mode 100644 index 00000000..1993b4e0 --- /dev/null +++ b/src/map.h @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _MAP_H +#define _MAP_H + +#include + +#include "object.h" + +char *alg2data_type(const char *alg); +int alg2object_type(const char *alg); +zpc_ec_curve_t alg2key_curve(const char *alg); +int alg2key_size(const char *alg); + +char *obj_data_type(const struct obj *alg); +int obj_object_type(const struct obj *alg); +zpc_ec_curve_t obj_key_curve(const struct obj *alg); +int obj_key_size(const struct obj *alg); + +#endif /* _MAP_H */ From 31eb1abd216df54881a35d3d9de23956dd39592d Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 03:22:34 -0500 Subject: [PATCH 14/39] cmake: Integrate mapping helpers Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d466687..0ba5b3a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,6 +153,7 @@ target_include_directories( set(ZPCPROVIDER_SOURCES src/provider.c src/object.c + src/map.c ) add_library( From 825ad0e3415b7cf139a7220f77414c67bdb0731b Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 20 Apr 2026 11:23:25 +0200 Subject: [PATCH 15/39] provider: Add store-loader Introduce a store-loader for hbkzpc-URI based keys. The store-loader creates a provider-specific key object and adds relevant information from the URI. Signed-off-by: Holger Dengler --- src/store.c | 331 ++++++++++++++++++++++++++++++++++++++++++++++ src/store.h | 10 ++ src/store_local.h | 17 +++ 3 files changed, 358 insertions(+) create mode 100644 src/store.c create mode 100644 src/store.h create mode 100644 src/store_local.h diff --git a/src/store.c b/src/store.c new file mode 100644 index 00000000..548b28d8 --- /dev/null +++ b/src/store.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include + +#include "provider.h" +#include "object.h" +#include "ossl.h" +#include "uri.h" +#include "map.h" +#include "store_local.h" + +struct store_ctx { + struct provider_ctx *pctx; + struct parsed_uri *puri; + + int type_exp; + bool eof; +}; + +#define DISP_STORE_FN(tname, name) DECL_DISPATCH_FUNC(store, tname, name) +DISP_STORE_FN(open, store_open); +#ifdef OSSL_FUNC_STORE_OPEN_EX +DISP_STORE_FN(open_ex, store_open_ex); +#endif +DISP_STORE_FN(load, store_load); +DISP_STORE_FN(eof, store_eof); +DISP_STORE_FN(close, store_close); +DISP_STORE_FN(set_ctx_params, store_set_ctx_params); +DISP_STORE_FN(settable_ctx_params, store_settable_ctx_params); +#undef DISP_STORE_FN + +static struct store_ctx *store_ctx_init(struct provider_ctx *pctx) +{ + struct store_ctx *sctx; + + sctx = OPENSSL_zalloc(sizeof(struct store_ctx)); + if (!sctx) + return NULL; + + sctx->pctx = pctx; + sctx->eof = false; + + return sctx; +} + +static void store_ctx_free(struct store_ctx *sctx) +{ + if (!sctx) + return; + + parsed_uri_free(sctx->puri); + OPENSSL_free(sctx); + + return; +} + +static int store_ctx_expect(struct store_ctx *sctx, int type_exp) +{ + int rv = OSSL_RV_OK; + + switch (type_exp) { + case OSSL_STORE_INFO_PUBKEY: + case OSSL_STORE_INFO_PKEY: + sctx->type_exp = type_exp; + break; + default: + rv = OSSL_RV_ERR; + break; + } + + return rv; +} + +static void *store_open(void *vpctx, const char *uri) +{ + struct store_ctx *sctx; + + sctx = store_ctx_init(vpctx); + if (!sctx) + return NULL; + + sctx->puri = parsed_uri_new(uri); + if (!sctx->puri) + goto err; + + return sctx; +err: + store_ctx_free(sctx); + return NULL; +} + +static void *store_open_ex(void *vpctx, const char *uri, + const OSSL_PARAM params[], + OSSL_PASSPHRASE_CALLBACK *pw_cb __unused, + void *pw_cbarg __unused) +{ + struct store_ctx *sctx; + + sctx = store_open(vpctx, uri); + if (!sctx) + return NULL; + + if (store_set_ctx_params(sctx, params) != OSSL_RV_OK) + goto err; + + return sctx; +err: + store_ctx_free(sctx); + return NULL; + +} + +static int attr2str(const struct attr *attr, char **str) +{ + if (!attr->value) + return OSSL_RV_OK; + + if (*str) + return OSSL_RV_ERR; + + *str = OPENSSL_strdup(attr->value); + return (*str) ? OSSL_RV_OK : OSSL_RV_ERR; +} + +static int attr2data(const struct attr *attr, struct data *data) +{ + size_t plen; + + if (!attr || !data) + return OSSL_RV_ERR; + + /* data already set */ + if (data->p || data->plen) + return OSSL_RV_ERR; + + if (!attr->value) + return OSSL_RV_OK; + plen = strlen(attr->value); + + if (OPENSSL_hexstr2buf_ex(NULL, 0, &plen, + attr->value, '\0') != 1) + return OSSL_RV_ERR; + + data->p = OPENSSL_zalloc(plen); + if (!data->p) + return OSSL_RV_ERR; + data->plen = plen; + + if (OPENSSL_hexstr2buf_ex(data->p, data->plen, &data->plen, + attr->value, '\0') != 1) { + OPENSSL_free(data->p); + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static struct obj *uri2obj(struct provider_ctx *pctx, const struct parsed_uri *puri) +{ + struct obj *obj; + + obj = obj_new(pctx); + if (!obj) + return NULL; + + if (attr2str(&puri->origin_type, &obj->origin_type) != OSSL_RV_OK) + goto err; + if (attr2str(&puri->origin_alg, &obj->origin_alg) != OSSL_RV_OK) + goto err; + if (attr2data(&puri->origin_blob, &obj->origin_blob) != OSSL_RV_OK) + goto err; + if (attr2data(&puri->origin_pubkey, &obj->origin_pubkey) != OSSL_RV_OK) + goto err; + if (attr2str(&puri->mkvp, &obj->mkvp) != OSSL_RV_OK) + goto err; + if (attr2str(&puri->apqns, &obj->apqns) != OSSL_RV_OK) + goto err; + + return obj; +err: + obj_free(obj); + return NULL; +} + +static int store_load(void *vsctx, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct store_ctx *sctx = (struct store_ctx *)vsctx; + bool public_only = false; + struct parsed_uri *puri; + int object_type; + + if (!sctx) + return OSSL_RV_ERR; + puri = sctx->puri; + sctx->eof = true; + + object_type = alg2object_type(puri->origin_alg.value); + if (object_type == OSSL_OBJECT_UNKNOWN) + return OSSL_RV_ERR; + + /* early checks */ + switch (sctx->type_exp) { + case OSSL_STORE_INFO_PKEY: + if (object_type != OSSL_OBJECT_PKEY) + return OSSL_RV_ERR; + break; + case OSSL_STORE_INFO_PUBKEY: + if (object_type != OSSL_OBJECT_PKEY) + return OSSL_RV_ERR; + public_only = true; + break; + case 0: + /* no expected type */ + break; + default: + return OSSL_RV_ERR; + } + + return store_load_uri(sctx->pctx, puri, public_only, + object_cb, object_cbarg, pw_cb, pw_cbarg); +} + +static int store_eof(void *vsctx) +{ + struct store_ctx *sctx = (struct store_ctx *)vsctx; + + if (!sctx) + return OSSL_RV_TRUE; + + return sctx->eof ? OSSL_RV_TRUE : OSSL_RV_FALSE; +} + +static int store_close(void *vsctx) +{ + store_ctx_free((struct store_ctx *)vsctx); + return OSSL_RV_OK; +} + +static int store_set_ctx_params(void *vsctx, const OSSL_PARAM params[]) +{ + struct store_ctx *sctx = (struct store_ctx *)vsctx; + const OSSL_PARAM *p; + int type_exp; + + if (!sctx) + return OSSL_RV_ERR; + + if (!params) + return OSSL_RV_OK; + + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_EXPECT); + if (p) { + if ((OSSL_PARAM_get_int(p, &type_exp) != OSSL_RV_OK) || + (store_ctx_expect(sctx, type_exp) != OSSL_RV_OK)) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *store_settable_ctx_params(void *pctx __unused) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_int(OSSL_STORE_PARAM_EXPECT, NULL), + OSSL_PARAM_END, + }; + return known_settable_ctx_params; +} + +int store_load_uri(struct provider_ctx *pctx, struct parsed_uri *puri, + bool public_only, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb __unused, + void *pw_cbarg __unused) +{ + OSSL_PARAM params[4]; + struct obj *obj; + char *data_type; + int object_type; + int rv; + + if (!pctx || !puri) + return OSSL_RV_ERR; + + object_type = alg2object_type(puri->origin_alg.value); + if (object_type == OSSL_OBJECT_UNKNOWN) + return OSSL_RV_ERR; + + data_type = alg2data_type(puri->origin_alg.value); + if (!data_type) + return OSSL_RV_ERR; + + obj = uri2obj(pctx, puri); + if (!obj) + return OSSL_RV_ERR; + obj->public_only = public_only; + + params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, + &object_type); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + data_type, 0); + params[2] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, + obj, sizeof(struct obj)); + params[3] = OSSL_PARAM_construct_end(); + + rv = object_cb(params, object_cbarg); + obj_free(obj); + return rv; +} + +static const OSSL_DISPATCH store_functions[] = { + DISPATCH_DEFN(STORE, OPEN, store_open), +#ifdef OSSL_FUNC_STORE_OPEN_EX + DISPATCH_DEFN(STORE, OPEN_EX, store_open_ex), +#endif + DISPATCH_DEFN(STORE, LOAD, store_load), + DISPATCH_DEFN(STORE, EOF, store_eof), + DISPATCH_DEFN(STORE, CLOSE, store_close), + DISPATCH_DEFN(STORE, SET_CTX_PARAMS, store_set_ctx_params), + DISPATCH_DEFN(STORE, SETTABLE_CTX_PARAMS, store_settable_ctx_params), + DISPATCH_END +}; + +const OSSL_ALGORITHM store_ops[] = { + { URI_PROTOCOL_PREFIX, "provider=" PROV_NAME, store_functions, "HBKZPC URI Store" }, + { NULL, NULL, NULL, NULL }, +}; diff --git a/src/store.h b/src/store.h new file mode 100644 index 00000000..a03c60f9 --- /dev/null +++ b/src/store.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _STORE_H +#define _STORE_H + +#include + +extern const OSSL_ALGORITHM store_ops[]; + +#endif /* _STORE_H */ diff --git a/src/store_local.h b/src/store_local.h new file mode 100644 index 00000000..69715033 --- /dev/null +++ b/src/store_local.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _STORE_LOCAL_H +#define _STORE_LOCAL_H + +#include +#include + +#include "provider.h" +#include "uri.h" + +int store_load_uri(struct provider_ctx *pctx, struct parsed_uri *puri, + bool public_only, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb , void *pw_cbarg); + +#endif /* _STORE_LOCAL_H */ From 3e707bb9bddbdbdac464a362166a89fc74664889 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:59:57 +0100 Subject: [PATCH 16/39] cmake: Integrate store-loader Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + src/provider.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ba5b3a1..8c908eba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -154,6 +154,7 @@ set(ZPCPROVIDER_SOURCES src/provider.c src/object.c src/map.c + src/store.c ) add_library( diff --git a/src/provider.c b/src/provider.c index 7ef3f669..8a9f4c1b 100644 --- a/src/provider.c +++ b/src/provider.c @@ -10,6 +10,7 @@ #include "ossl.h" #include "provider.h" +#include "store.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -274,6 +275,9 @@ static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, return NULL; switch (operation_id) { + case OSSL_OP_STORE: + ops = store_ops; + break; default: ops = NULL; goto out; From e8b288d76dc00d8470e19b61debb3d8cda99ee2c Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 18 Feb 2026 04:37:32 -0500 Subject: [PATCH 17/39] provider: Add asymmetric key management Introduce a asymmetric key management to map the provider-specific key object to a intern zpc-key. Not supported: - key generation - key import/export Signed-off-by: Holger Dengler --- src/keymgmt.c | 606 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/keymgmt.h | 10 + src/object.c | 3 + src/object.h | 2 + 4 files changed, 621 insertions(+) create mode 100644 src/keymgmt.c create mode 100644 src/keymgmt.h diff --git a/src/keymgmt.c b/src/keymgmt.c new file mode 100644 index 00000000..1a3759a0 --- /dev/null +++ b/src/keymgmt.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include +#include +#include +#include + +#include "provider.h" +#include "object.h" +#include "ossl.h" +#include "map.h" +#include "zpc/ecc_key.h" +#include "zpc/error.h" + +static struct { + const char *str; + int type; +} type_map[] = { + { .str = "uv", .type = ZPC_EC_KEY_TYPE_PVSECRET, }, + { .str = "cca", .type = ZPC_EC_KEY_TYPE_CCA, }, + { .str = "ep11", .type = ZPC_EC_KEY_TYPE_EP11, }, + { 0 }, +}; + +static int str2type(const char *str) +{ + for (int i = 0; type_map[i].str; i++) { + if (OPENSSL_strcasecmp(type_map[i].str, str) == 0) + return type_map[i].type; + } + return 0; +} + +static struct { + const char *alg; + int bits; + int secbits; + int sigsz; +} alg_param_map[] = { + { + .alg = SN_X9_62_prime256v1, + .bits = 256, .secbits = 128, .sigsz = 64 + 8, + }, { + .alg = SN_secp384r1, + .bits = 384, .secbits = 192, .sigsz = 96 + 8, + }, { + .alg = SN_secp521r1, + .bits = 521, .secbits = 256, .sigsz = 132 + 8 + }, { + .alg = SN_ED25519, + .bits = 256, .secbits = 128, .sigsz = 64 + }, { + .alg = SN_ED448, + .bits = 456, .secbits = 224, .sigsz = 114}, + { 0 }, +}; + +static int alg2param(const char *alg, int *bits, int *secbits, int *sigsz) +{ + int i; + + if (!alg) + return OSSL_RV_ERR; + + for (i = 0; alg_param_map[i].alg; i++) { + if (OPENSSL_strcasecmp(alg_param_map[i].alg, alg) == 0) + goto found; + } + /* not found */ + return OSSL_RV_ERR; +found: + if (bits) + *bits = alg_param_map[i].bits; + if (secbits) + *secbits = alg_param_map[i].secbits; + if (sigsz) + *sigsz = alg_param_map[i].sigsz; + + return OSSL_RV_OK; +} + +static int zpc_key_update(struct obj *obj) +{ + struct zpc_ec_key *key = NULL; + int rc; + + if ((rc = zpc_ec_key_alloc(&key))) + goto err; + + if ((rc = zpc_ec_key_set_type(key, str2type(obj->origin_type)))) + goto err; + + if ((rc = zpc_ec_key_set_curve(key, obj_key_curve(obj)))) + goto err; + + if ((rc = zpc_ec_key_import(key, obj->origin_blob.p, + obj->origin_blob.plen))) + goto err; + + if (strcmp(obj->origin_type, "uv") == 0 && + obj->origin_pubkey.p) { + /* uv only: import pubkey */ + if ((rc = zpc_ec_key_import_clear(key, obj->origin_pubkey.p, + obj->origin_pubkey.plen, + NULL, 0))) + goto err; + } else { + if (obj->mkvp && (rc = zpc_ec_key_set_mkvp(key, obj->mkvp))) + goto err; + /* TODO: apqns */ + } + + obj->ec_key = key; + return OSSL_RV_OK; +err: + PROV_ERR_raise(obj->pctx, rc); + zpc_ec_key_free(&key); + return OSSL_RV_ERR; +} + +static int ec_pub_uncomp(unsigned char *raw, size_t rawlen, + unsigned char **pub, size_t *publen) +{ + unsigned char *p; + size_t plen; + + if (!raw || !rawlen) + return OSSL_RV_ERR; + + plen = rawlen + 1; + p = OPENSSL_malloc(plen); + if (!p) + return OSSL_RV_ERR; + + p[0] = POINT_CONVERSION_UNCOMPRESSED; + memcpy(&p[1], raw, rawlen); + + if(pub) + *pub = p; + if (publen) + *publen = plen; + + return OSSL_RV_OK; +} + +static int ec_enc_pubkey_param(struct obj *obj, OSSL_PARAM *p) +{ + unsigned char *pub = NULL; + size_t publen; + int rv; + + if (!obj || !obj->origin_pubkey.p) + return OSSL_RV_ERR; + + if (ec_pub_uncomp(obj->origin_pubkey.p, obj->origin_pubkey.plen, + &pub, &publen) != OSSL_RV_OK) + return OSSL_RV_ERR; + + rv = OSSL_PARAM_set_octet_string(p, pub, publen); + OPENSSL_free(pub); + return rv; +} + +enum ec_coord { + EC_X, + EC_Y, +}; + +static int ec_pubkey_coord_param(struct obj *obj, enum ec_coord coord, + OSSL_PARAM *p) +{ + const unsigned char *raw; + size_t rawlen; + BIGNUM *c; + int rv; + + if (!obj || + !obj->origin_pubkey.p) + return OSSL_RV_ERR; + + rawlen = obj->origin_pubkey.plen / 2; + switch (coord) { + case EC_X: + raw = obj->origin_pubkey.p; + break; + case EC_Y: + raw = obj->origin_pubkey.p + rawlen; + break; + default: + return OSSL_RV_ERR; + } + + if (!(c = BN_bin2bn(raw, rawlen, NULL))) + return OSSL_RV_ERR; + + rv = OSSL_PARAM_set_BN(p, c); + BN_free(c); + return rv; +} + +static int kmgmt_get_params(struct obj *obj, OSSL_PARAM params[]) +{ + int bits, secbits, sigsz; + OSSL_PARAM *p; + + if (!obj || + alg2param(obj->origin_alg, &bits, &secbits, &sigsz) != OSSL_RV_OK) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); + if (p) { + if (OSSL_PARAM_set_int(p, bits) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS); + if (p) { + if (OSSL_PARAM_set_int(p, secbits) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); + if (p) { + if (OSSL_PARAM_set_int(p, sigsz) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_GROUP_NAME); + if (p) { + if(OSSL_PARAM_set_utf8_string(p, obj->origin_alg) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static int kmgmt_export(struct obj *obj, int selection, + OSSL_CALLBACK *param_cb, void *cbarg, + bool raw) +{ + int bits, secbits, sigsz, rv = OSSL_RV_ERR; + unsigned char *pub, *p = NULL; + OSSL_PARAM_BLD *param_bld; + OSSL_PARAM *params = NULL; + size_t publen; + + if (!obj || + (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) + return OSSL_RV_ERR; + + if (alg2param(obj->origin_alg, &bits, &secbits, &sigsz) != OSSL_RV_OK) + goto out; + + if (!(param_bld = OSSL_PARAM_BLD_new())) + goto out; + + if (raw) { + publen = obj->origin_pubkey.plen; + pub = obj->origin_pubkey.p; + } else { + if (ec_pub_uncomp(obj->origin_pubkey.p, + obj->origin_pubkey.plen, + &p, &publen) != OSSL_RV_OK) + goto out; + pub = p; + } + + if (selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) { + if (OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME, + obj->origin_alg, strlen(obj->origin_alg)) != OSSL_RV_OK) + goto out; + + if (OSSL_PARAM_BLD_push_int(param_bld, OSSL_PKEY_PARAM_BITS, bits) != OSSL_RV_OK) + goto out; + + if (OSSL_PARAM_BLD_push_int(param_bld, OSSL_PKEY_PARAM_SECURITY_BITS, secbits) != OSSL_RV_OK) + goto out; + + if (OSSL_PARAM_BLD_push_int(param_bld, OSSL_PKEY_PARAM_MAX_SIZE, sigsz) != OSSL_RV_OK) + goto out; + } + + if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { + if (OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY, + pub, publen) != OSSL_RV_OK) + goto out; + } + + if (!(params = OSSL_PARAM_BLD_to_param(param_bld))) + goto out; + + rv = param_cb(params, cbarg); +out: + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(param_bld); + return rv; +} + +#define DECL_KMGMT_FN(tname, name) DECL_DISPATCH_FUNC(keymgmt, tname, name) +DECL_KMGMT_FN(new, kmgmt_new); +DECL_KMGMT_FN(free, kmgmt_free); +DECL_KMGMT_FN(load, kmgmt_load); +DECL_KMGMT_FN(has, kmgmt_has); +DECL_KMGMT_FN(export, ec_export); +DECL_KMGMT_FN(export, ed_export); +DECL_KMGMT_FN(export_types_ex, kmgmt_export_types_ex); +DECL_KMGMT_FN(query_operation_name, ec_query_operation_name); +DECL_KMGMT_FN(query_operation_name, ed25519_query_operation_name); +DECL_KMGMT_FN(query_operation_name, ed448_query_operation_name); +DECL_KMGMT_FN(gettable_params, ec_gettable_params); +DECL_KMGMT_FN(gettable_params, ed_gettable_params); +DECL_KMGMT_FN(get_params, ec_get_params); +DECL_KMGMT_FN(get_params, ed_get_params); +DECL_KMGMT_FN(settable_params, kmgmt_settable_params); +DECL_KMGMT_FN(set_params, kmgmt_set_params); +#undef DECL_SKMGMT_FN + +static void *kmgmt_new(void *provctx) +{ + return obj_new((struct provider_ctx *)provctx); +} + +static void kmgmt_free(void *keydata) +{ + struct obj *obj = (struct obj *)keydata; + + zpc_ec_key_free(&obj->ec_key); + obj_free(obj); +} + +static void *kmgmt_load(const void *reference, size_t reference_sz) +{ + struct obj *obj; + + if (!reference || reference_sz != sizeof(struct obj)) + return NULL; + obj = (struct obj *)reference; + + if (zpc_key_update(obj) == OSSL_RV_ERR) + return NULL; + + return obj_get(obj); +} + +static int kmgmt_has(const void *keydata, int selection) +{ + struct obj *obj = (struct obj *)keydata; + int rv = OSSL_RV_TRUE; + + if (!obj) + return OSSL_RV_FALSE; + + if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { + if (obj->public_only || !obj->origin_blob.p || !obj->ec_key) + rv = OSSL_RV_FALSE; + } + + if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { + if (!obj->origin_pubkey.p) + rv = OSSL_RV_FALSE; + } + + return rv; +} + +static int ec_export(void *keydata, int selection, + OSSL_CALLBACK *param_cb, void *cbarg) +{ + return kmgmt_export(keydata, selection, param_cb, cbarg, false); +} + +static int ed_export(void *keydata, int selection, + OSSL_CALLBACK *param_cb, void *cbarg) +{ + return kmgmt_export(keydata, selection, param_cb, cbarg, true); +} + +const OSSL_PARAM *kmgmt_export_types_ex(void *provctx __unused, int selection) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_END, + }; + int idx = 2; /* none */ + + if (selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + idx = 1; /* dom-param only */ + if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + idx = 0; /* dom-param + pubkey */ + + return ¶ms[idx]; +} + +static const char *ec_query_operation_name(int operation_id) +{ + return (operation_id == OSSL_OP_SIGNATURE) ? + PROV_NAME_ECDSA : NULL; +} + +static const char *ed25519_query_operation_name(int operation_id) +{ + return (operation_id == OSSL_OP_SIGNATURE) ? + PROV_NAME_ED25519 : NULL; +} + +static const char *ed448_query_operation_name(int operation_id) +{ + return (operation_id == OSSL_OP_SIGNATURE) ? + PROV_NAME_ED448 : NULL; +} + +static const OSSL_PARAM *ec_gettable_params(void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + /* common */ + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + /* ec-specific */ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ec_get_params(void *keydata, OSSL_PARAM params[]) +{ + struct obj *obj = (struct obj *)keydata; + OSSL_PARAM *p; + + if (!obj) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, + OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT); + if (p) { + if (OSSL_PARAM_set_utf8_string(p, "uncompressed") != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p) { + if (ec_enc_pubkey_param(obj, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p) { + if (ec_enc_pubkey_param(obj, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_X); + if (p) { + if (ec_pubkey_coord_param(obj, EC_X, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_Y); + if (p) { + if (ec_pubkey_coord_param(obj, EC_Y, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return kmgmt_get_params(obj, params); +} + +static const OSSL_PARAM *ed_gettable_params(void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + /* common */ + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + /* ed-specific */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ed_get_params(void *keydata, OSSL_PARAM params[]) +{ + struct obj *obj = (struct obj *)keydata; + OSSL_PARAM *p; + + if (!obj) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p) { + if (!obj->origin_pubkey.p || + OSSL_PARAM_set_octet_string(p, obj->origin_pubkey.p, + obj->origin_pubkey.plen) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MANDATORY_DIGEST); + if (p) { + if (OSSL_PARAM_set_utf8_string(p, "") != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return kmgmt_get_params(obj, params); +} + +static const OSSL_PARAM *kmgmt_settable_params(void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + /* none */ + OSSL_PARAM_END, + }; + return params; +} + +static int kmgmt_set_params(void *keydata __unused, + const OSSL_PARAM params[]) +{ + const char *fmt = NULL; + const OSSL_PARAM *p; + int include_public; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC); + if (p) { + if (OSSL_PARAM_get_int(p, &include_public) != OSSL_RV_OK || + !include_public) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT); + if (p) { + if (OSSL_PARAM_get_utf8_string_ptr(p, &fmt) != OSSL_RV_OK || + !fmt || + OPENSSL_strcasecmp(fmt, OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED) != 0) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_DISPATCH kmgmt_ecdsa_functions[] = { + DISPATCH_DEFN(KEYMGMT, NEW, kmgmt_new), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, EXPORT, ec_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), + DISPATCH_DEFN(KEYMGMT, QUERY_OPERATION_NAME, ec_query_operation_name), + DISPATCH_DEFN(KEYMGMT, GETTABLE_PARAMS, ec_gettable_params), + DISPATCH_DEFN(KEYMGMT, GET_PARAMS, ec_get_params), + DISPATCH_DEFN(KEYMGMT, SETTABLE_PARAMS, kmgmt_settable_params), + DISPATCH_DEFN(KEYMGMT, SET_PARAMS, kmgmt_set_params), + DISPATCH_END, +}; + +static const OSSL_DISPATCH kmgmt_ed25519_functions[] = { + DISPATCH_DEFN(KEYMGMT, NEW, kmgmt_new), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, EXPORT, ed_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), + DISPATCH_DEFN(KEYMGMT, QUERY_OPERATION_NAME, ed25519_query_operation_name), + DISPATCH_DEFN(KEYMGMT, GETTABLE_PARAMS, ed_gettable_params), + DISPATCH_DEFN(KEYMGMT, GET_PARAMS, ed_get_params), + DISPATCH_DEFN(KEYMGMT, SETTABLE_PARAMS, kmgmt_settable_params), + DISPATCH_DEFN(KEYMGMT, SET_PARAMS, kmgmt_set_params), + DISPATCH_END, +}; + +static const OSSL_DISPATCH kmgmt_ed448_functions[] = { + DISPATCH_DEFN(KEYMGMT, NEW, kmgmt_new), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, EXPORT, ed_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), + DISPATCH_DEFN(KEYMGMT, QUERY_OPERATION_NAME, ed448_query_operation_name), + DISPATCH_DEFN(KEYMGMT, GETTABLE_PARAMS, ed_gettable_params), + DISPATCH_DEFN(KEYMGMT, GET_PARAMS, ed_get_params), + DISPATCH_DEFN(KEYMGMT, SETTABLE_PARAMS, kmgmt_settable_params), + DISPATCH_DEFN(KEYMGMT, SET_PARAMS, kmgmt_set_params), + DISPATCH_END, +}; + +const OSSL_ALGORITHM keymgmt_ops[] = { + ALGORITHM_DEFN(PROV_NAME_EC, PROV_PROP, kmgmt_ecdsa_functions, + PROV_DESC_EC), + ALGORITHM_DEFN(PROV_NAME_ED25519, PROV_PROP, kmgmt_ed25519_functions, + PROV_DESC_ED25519), + ALGORITHM_DEFN(PROV_NAME_ED448, PROV_PROP, kmgmt_ed448_functions, + PROV_DESC_ED448), + ALGORITHM_END, +}; diff --git a/src/keymgmt.h b/src/keymgmt.h new file mode 100644 index 00000000..359ec42f --- /dev/null +++ b/src/keymgmt.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _KEYMGMT_H +#define _KEYMGMT_H + +#include + +extern const OSSL_ALGORITHM keymgmt_ops[]; + +#endif /* _KEYMGMT_H */ diff --git a/src/object.c b/src/object.c index 89349683..3cb52ef9 100644 --- a/src/object.c +++ b/src/object.c @@ -3,9 +3,12 @@ #include #include "object.h" +#include "zpc/ecc_key.h" static void _obj_free(struct obj *obj) { + zpc_ec_key_free(&obj->ec_key); + OPENSSL_free(obj->origin_type); OPENSSL_free(obj->origin_alg); OPENSSL_free(obj->origin_blob.p); diff --git a/src/object.h b/src/object.h index 06a738e4..03942bf0 100644 --- a/src/object.h +++ b/src/object.h @@ -6,6 +6,7 @@ #include #include "provider.h" +#include "zpc/ecc_key.h" struct data { size_t plen; @@ -21,6 +22,7 @@ struct obj { char *id; /* zpc keys */ + struct zpc_ec_key *ec_key; /* origin path attrs */ char *origin_type; From f07093eb99ddb099fba65dac4bc43b574b6f83bd Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 10:08:31 +0200 Subject: [PATCH 18/39] cmake: Integrate asymmetric key management Signed-off-by: Holger Dengler --- CMakeLists.txt | 2 ++ src/provider.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c908eba..8fb82474 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,6 +155,7 @@ set(ZPCPROVIDER_SOURCES src/object.c src/map.c src/store.c + src/keymgmt.c ) add_library( @@ -181,6 +182,7 @@ target_link_libraries( PRIVATE OpenSSL::Crypto $ + $ ) target_link_options( diff --git a/src/provider.c b/src/provider.c index 8a9f4c1b..f9fd6a2d 100644 --- a/src/provider.c +++ b/src/provider.c @@ -11,6 +11,7 @@ #include "ossl.h" #include "provider.h" #include "store.h" +#include "keymgmt.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -278,6 +279,9 @@ static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, case OSSL_OP_STORE: ops = store_ops; break; + case OSSL_OP_KEYMGMT: + ops = keymgmt_ops; + break; default: ops = NULL; goto out; From 144b3f5789c401e3e7fad91ac84c4c1bcda8783e Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 27 Apr 2026 16:53:47 +0200 Subject: [PATCH 19/39] provider: Add algorithm-id helpers Add helpers to generate DER-encoded algorithm-ids based on key and digest information. Signed-off-by: Holger Dengler --- src/algid.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/algid.h | 11 +++++ 2 files changed, 133 insertions(+) create mode 100644 src/algid.c create mode 100644 src/algid.h diff --git a/src/algid.c b/src/algid.c new file mode 100644 index 00000000..c04494bb --- /dev/null +++ b/src/algid.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include + +#include "provider.h" +#include "ossl.h" +#include "algid.h" + +#define DER_OID_HDR(OLEN) 0x30, (OLEN + 2), 0x06, (OLEN) +#define OID_ECDSA_SHA1 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04 +#define OID_ECDSA_SHA2 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03 +#define OID_ECDSA_SHA3 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03 +#define OID_EDDSA 0x2B, 0x65 + +#define DER_ECDSA_SHA1 DER_OID_HDR(7), OID_ECDSA_SHA1, 0x01 +#define DER_ECDSA_SHA2_224 DER_OID_HDR(8), OID_ECDSA_SHA2, 0x01 +#define DER_ECDSA_SHA2_256 DER_OID_HDR(8), OID_ECDSA_SHA2, 0x02 +#define DER_ECDSA_SHA2_384 DER_OID_HDR(8), OID_ECDSA_SHA2, 0x03 +#define DER_ECDSA_SHA2_512 DER_OID_HDR(8), OID_ECDSA_SHA2, 0x04 +#define DER_ECDSA_SHA3_224 DER_OID_HDR(9), OID_ECDSA_SHA3, 0x09 +#define DER_ECDSA_SHA3_256 DER_OID_HDR(9), OID_ECDSA_SHA3, 0x0A +#define DER_ECDSA_SHA3_384 DER_OID_HDR(9), OID_ECDSA_SHA3, 0x0B +#define DER_ECDSA_SHA3_512 DER_OID_HDR(9), OID_ECDSA_SHA3, 0x0C +#define DER_EDDSA_25519 DER_OID_HDR(3), OID_EDDSA, 0x70 +#define DER_EDDSA_448 DER_OID_HDR(3), OID_EDDSA, 0x71 + +struct ecdsa_algid { + int type; + const unsigned char *der; + size_t derlen; +}; +#define ECDSA_ALGID(md, MD) { \ + .type = NID_##md, \ + .der = der_ECDSA_##MD, \ + .derlen = sizeof(der_ECDSA_##MD) \ +} + +struct eddsa_algid { + const char *alg; + const unsigned char *der; + size_t derlen; +}; +#define EDDSA_ALGID(curve) { \ + .alg = SN_ED##curve, \ + .der = der_EDDSA_##curve, \ + .derlen = sizeof(der_EDDSA_##curve) \ +} + +#define DER(TYPE, SUBTYPE) \ + static unsigned char der_##TYPE##_##SUBTYPE[] = { DER_##TYPE##_##SUBTYPE } + +DER(ECDSA, SHA1); +DER(ECDSA, SHA2_224); +DER(ECDSA, SHA2_256); +DER(ECDSA, SHA2_384); +DER(ECDSA, SHA2_512); +DER(ECDSA, SHA3_224); +DER(ECDSA, SHA3_256); +DER(ECDSA, SHA3_384); +DER(ECDSA, SHA3_512); + +DER(EDDSA, 25519); +DER(EDDSA, 448); + +static struct ecdsa_algid ecdsa_algid_map[] = { + ECDSA_ALGID(sha1, SHA1), + ECDSA_ALGID(sha224, SHA2_224), + ECDSA_ALGID(sha256, SHA2_256), + ECDSA_ALGID(sha384, SHA2_384), + ECDSA_ALGID(sha512, SHA2_512), + ECDSA_ALGID(sha3_224, SHA3_224), + ECDSA_ALGID(sha3_256, SHA3_256), + ECDSA_ALGID(sha3_384, SHA3_384), + ECDSA_ALGID(sha3_512, SHA3_512), +}; + +static struct eddsa_algid eddsa_algid_map[] = { + EDDSA_ALGID(25519), + EDDSA_ALGID(448), +}; + +int algid_ecdsa(int type, OSSL_PARAM *p) +{ + struct ecdsa_algid *a = NULL; + int rv = OSSL_RV_ERR; + size_t i; + + for (i = 0; i < ARRAY_SIZE(ecdsa_algid_map); i++) { + a = &ecdsa_algid_map[i]; + if (type != a->type) + continue; + break; + } + if (i == ARRAY_SIZE(ecdsa_algid_map)) + goto out; + + rv = OSSL_PARAM_set_octet_string(p, a->der, a->derlen); +out: + return rv; +} + +int algid_eddsa(const char *alg, OSSL_PARAM *p) +{ + struct eddsa_algid *a = NULL; + int rv = OSSL_RV_ERR; + size_t i; + + for (i = 0; i < ARRAY_SIZE(eddsa_algid_map); i++) { + a = &eddsa_algid_map[i]; + if (OPENSSL_strcasecmp(alg, a->alg) == 0) + continue; + break; + } + if (i == ARRAY_SIZE(eddsa_algid_map)) + goto out; + + rv = OSSL_PARAM_set_octet_string(p, a->der, a->derlen); +out: + return rv; +} diff --git a/src/algid.h b/src/algid.h new file mode 100644 index 00000000..1f05ba57 --- /dev/null +++ b/src/algid.h @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _ALGID_H +#define _ALGID_H + +#include + +int algid_ecdsa(int type, OSSL_PARAM *p); +int algid_eddsa(const char *alg, OSSL_PARAM *p); + +#endif /* _ALGID_H */ From 7aacb4a32eef3ed6903ae5920f8037b20fbfd1f6 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 27 Apr 2026 16:54:36 +0200 Subject: [PATCH 20/39] cmake: Integrate algorithm-id helpers Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fb82474..0cb7a255 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,7 @@ set(ZPCPROVIDER_SOURCES src/map.c src/store.c src/keymgmt.c + src/algid.c ) add_library( From 26ef55264d45993bd89f9829234ae200c298efcb Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 9 Mar 2026 12:37:49 -0400 Subject: [PATCH 21/39] provider: Add signature algorithms Add signature algorithms for sign/verify with ECDSA and EDDSA keys. Signed-off-by: Holger Dengler --- src/signature.c | 784 ++++++++++++++++++++++++++++++++++++++++++++++++ src/signature.h | 10 + 2 files changed, 794 insertions(+) create mode 100644 src/signature.c create mode 100644 src/signature.h diff --git a/src/signature.c b/src/signature.c new file mode 100644 index 00000000..32150d43 --- /dev/null +++ b/src/signature.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include +#include +#include + +#include "provider.h" +#include "signature.h" +#include "object.h" +#include "ossl.h" +#include "map.h" +#include "algid.h" +#include "zpc/ecdsa_ctx.h" + +/* REVISIT deterministic nonce support (OSSL_SIGNATURE_PARAM_NONCE_TYPE) */ + +#define ASN1_SIG_HDR 8 + +typedef int (*set_ctx_params_fn)(void *, const OSSL_PARAM *); + +enum sig_op { + SIG_OP_UNDEF = 0, + SIG_OP_SIGN, + SIG_OP_VERIFY, +}; + +enum sig_fmt { + SIG_FMT_UNDEF = 0, + SIG_FMT_RAW, + SIG_FMT_DER, +}; + +static const int ecdsa_curves[] = { + ZPC_EC_CURVE_P256, + ZPC_EC_CURVE_P384, + ZPC_EC_CURVE_P521, +}; + +static const int ed25519_curves[] = { + ZPC_EC_CURVE_ED25519, +}; + +static const int ed448_curves[] = { + ZPC_EC_CURVE_ED448, +}; + +/* supported instance strings */ +static const char *ed25519 = "Ed25519"; +static const char *ed448 = "Ed448"; +static const unsigned char *edctx = { 0 }; + +struct sig_ctx { + struct provider_ctx *pctx; + char *propq; + + EVP_MD_CTX *fwd_md_ctx; + struct zpc_ecdsa_ctx *zpc_ctx; + + enum sig_op op; + enum sig_fmt fmt; + + const int *curves; + size_t curves_len; + + set_ctx_params_fn set_ctx_params; + + const char *ed_instance; + const unsigned char *ed_ctx; + size_t ed_ctxlen; +}; + +static bool valid_curve(int curve, const int *curves, size_t curves_len) +{ + for (size_t i = 0; i < curves_len; i++) + if (curve == curves[i]) + return true; + + return false; +} + +static int raw2der(const unsigned char *raw, size_t rawlen, + unsigned char *der, size_t *derlen) +{ + BIGNUM *r = NULL, *s = NULL; + ECDSA_SIG *ec_sig = NULL; + int rv = OSSL_RV_ERR; + size_t _derlen; + + if (!raw || !rawlen || !derlen || + (*derlen < (rawlen + ASN1_SIG_HDR))) + goto out; + + ec_sig = ECDSA_SIG_new(); + if (!ec_sig) + goto out; + + if (!(r = BN_bin2bn(raw, rawlen / 2, NULL)) || + !(s = BN_bin2bn(raw + rawlen / 2, rawlen / 2, NULL)) || + ECDSA_SIG_set0(ec_sig, r, s) != OSSL_RV_OK) { + BN_clear_free(r); + BN_clear_free(s); + goto out; + } + + _derlen = i2d_ECDSA_SIG(ec_sig, &der); + if (_derlen <= 0) + goto out; + + *derlen = _derlen; + rv = OSSL_RV_OK; +out: + ECDSA_SIG_free(ec_sig); + return rv; +} + +static int der2raw(const unsigned char *der, size_t derlen, + unsigned char *raw, size_t *rawlen) +{ + ECDSA_SIG *ec_sig = NULL; + int rv = OSSL_RV_ERR; + const BIGNUM *r, *s; + size_t _rawlen; + + if (!der || !derlen || !rawlen) + goto out; + _rawlen = *rawlen; + + ec_sig = d2i_ECDSA_SIG(NULL, &der, derlen); + if (!ec_sig) + goto out; + + r = ECDSA_SIG_get0_r(ec_sig); + s = ECDSA_SIG_get0_s(ec_sig); + if (!r || !s) + goto out; + + if ((BN_bn2binpad(r, raw, _rawlen / 2) == -1) || + (BN_bn2binpad(s, raw + _rawlen / 2, _rawlen / 2) == -1)) + goto out; + + rv = OSSL_RV_OK; +out: + ECDSA_SIG_free(ec_sig); + return rv; +} + +static struct sig_ctx *sig_newctx(struct provider_ctx *pctx, const char *propq, + const int *curves, size_t curves_len, + enum sig_fmt fmt, set_ctx_params_fn set_ctx_params, + const char *ed_instance, + const unsigned char *ed_ctx, size_t ed_ctxlen) +{ + struct zpc_ecdsa_ctx *zpc_ctx = NULL; + struct sig_ctx *sctx = NULL; + char *pq; + int rc; + + if (!pctx || fmt == SIG_FMT_UNDEF) + goto err; + + rc = zpc_ecdsa_ctx_alloc(&zpc_ctx); + if (rc) { + PROV_ERR_raise(pctx, rc); + goto err; + } + + sctx = OPENSSL_zalloc(sizeof(struct sig_ctx)); + if (!sctx) + goto err; + + pq = OPENSSL_strdup(propq); + if (pq) + goto err; + + sctx->pctx = pctx; + sctx->zpc_ctx = zpc_ctx; + sctx->propq = pq; + sctx->curves = curves; + sctx->curves_len = curves_len; + sctx->fmt = fmt; + sctx->set_ctx_params = set_ctx_params; + sctx->ed_instance = ed_instance; + sctx->ed_ctx = ed_ctx; + sctx->ed_ctxlen = ed_ctxlen; + + return sctx; +err: + OPENSSL_free(sctx); + zpc_ecdsa_ctx_free(&zpc_ctx); + return NULL; +} + +static int sig_sctx_set_md(struct sig_ctx *sctx, const char *mdname, + const OSSL_PARAM params[]) +{ + EVP_MD_CTX *md_ctx = NULL; + const EVP_MD *md; + + if (!sctx) + return OSSL_RV_ERR; + + if (mdname) { + if (!(md = EVP_MD_fetch(sctx->pctx->libctx, mdname, PROV_PROP_FWD)) || + !(md_ctx = EVP_MD_CTX_new()) || + (EVP_DigestInit_ex2(md_ctx, md, params) != OSSL_RV_OK)) + return OSSL_RV_ERR; + } + + EVP_MD_CTX_free(sctx->fwd_md_ctx); + sctx->fwd_md_ctx = md_ctx; + + return OSSL_RV_OK; +} + +static int sig_init(struct sig_ctx *sctx, struct obj *obj, + const OSSL_PARAM params[], + enum sig_op op) +{ + int rc; + + if (!sctx) + return OSSL_RV_ERR; + + if (obj) { + if (!valid_curve(obj_key_curve(obj), + sctx->curves, sctx->curves_len)) + return OSSL_RV_ERR; + + if ((rc = zpc_ecdsa_ctx_set_key(sctx->zpc_ctx, obj->ec_key))) { + PROV_ERR_raise(sctx->pctx, rc); + return OSSL_RV_ERR; + } + } + + sctx->op = op; + + return sctx->set_ctx_params ? + sctx->set_ctx_params(sctx, params) : + OSSL_RV_OK; +} + +static int sig_digest_init(struct sig_ctx *sctx, const char *mdname, + struct obj *obj, const OSSL_PARAM params[], + enum sig_op op) +{ + int rv; + + if (!sctx) + return OSSL_RV_ERR; + + rv = sctx->fwd_md_ctx ? + EVP_MD_CTX_reset(sctx->fwd_md_ctx) : + sig_sctx_set_md(sctx, mdname, params); + if (rv != OSSL_RV_OK) + return rv; + + return sig_init(sctx, obj, params, op); +} + +static int sig_sign_raw(struct sig_ctx *sctx, + unsigned char *sig, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + *siglen = sigsize; + return zpc_ecdsa_sign(sctx->zpc_ctx, tbs, tbslen, sig, siglen) + ? OSSL_RV_ERR + : OSSL_RV_OK; +} + +static int sig_sign_der(struct sig_ctx *sctx, + unsigned char *sig, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + unsigned char raw[EVP_MAX_MD_SIZE]; + unsigned char *_sig; + size_t rawlen; + int rv; + + rawlen = MIN(sigsize, EVP_MAX_MD_SIZE); + _sig = sig ? raw : NULL; + + rv = sig_sign_raw(sctx, _sig, &rawlen, rawlen, tbs, tbslen); + if (rv != OSSL_RV_OK) + return rv; + + if (!sig) + *siglen = rawlen + ASN1_SIG_HDR; + else + rv = raw2der(raw, rawlen, sig, siglen); + + return rv; +} + +static int sig_verify_raw(struct sig_ctx *sctx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + if (!sctx || + !sctx->zpc_ctx || + sctx->op != SIG_OP_VERIFY) + return OSSL_RV_ERR; + + return zpc_ecdsa_verify(sctx->zpc_ctx, tbs, tbslen, sig, siglen) + ? OSSL_RV_ERR + : OSSL_RV_OK; +} + +static int sig_verify_der(struct sig_ctx *sctx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + unsigned char raw[EVP_MAX_MD_SIZE]; + size_t rawlen; + int rv; + + /* calculate raw signature size */ + if (zpc_ecdsa_sign(sctx->zpc_ctx, tbs, tbslen, NULL, &rawlen)) + return OSSL_RV_ERR; + + rv = der2raw(sig, siglen, raw, &rawlen); + if (rv != OSSL_RV_OK) + return rv; + + return sig_verify_raw(sctx, raw, rawlen, tbs, tbslen); +} + +#define DISP_SIG(tname, name) DECL_DISPATCH_FUNC(signature, tname, name) +DISP_SIG(newctx, ecdsa_newctx); +#if 0 +DISP_SIG(newctx, ed25519_newctx); +DISP_SIG(newctx, ed448_newctx); +#endif +DISP_SIG(freectx, sig_freectx); + +DISP_SIG(sign_init, sig_sign_init); +DISP_SIG(sign, sig_sign); +DISP_SIG(verify_init, sig_verify_init); +DISP_SIG(verify, sig_verify); + +DISP_SIG(digest_sign_init, sig_digest_sign_init); +DISP_SIG(digest_sign_update, sig_digest_update); +DISP_SIG(digest_sign_final, sig_digest_sign_final); +DISP_SIG(digest_verify_init, sig_digest_verify_init); +DISP_SIG(digest_verify_update, sig_digest_update); +DISP_SIG(digest_verify_final, sig_digest_verify_final); + +DISP_SIG(gettable_ctx_params, ecdsa_gettable_ctx_params); +DISP_SIG(get_ctx_params, ecdsa_get_ctx_params); +DISP_SIG(settable_ctx_params, ecdsa_settable_ctx_params); +DISP_SIG(set_ctx_params, ecdsa_set_ctx_params); + +DISP_SIG(gettable_ctx_params, eddsa_gettable_ctx_params); +DISP_SIG(get_ctx_params, eddsa_get_ctx_params); +DISP_SIG(settable_ctx_params, eddsa_settable_ctx_params); +DISP_SIG(set_ctx_params, eddsa_set_ctx_params); + +#undef DISP_SIG + +/* dispatch */ +static void *ecdsa_newctx(void *vpctx, const char *propq) +{ + return sig_newctx(vpctx, propq, ecdsa_curves, ARRAY_SIZE(ecdsa_curves), + SIG_FMT_DER, ecdsa_set_ctx_params, NULL, NULL, 0); +} + +static void *ed25519_newctx(void *vpctx, const char *propq) +{ + return sig_newctx(vpctx, propq, ed25519_curves, ARRAY_SIZE(ed25519_curves), + SIG_FMT_RAW, eddsa_set_ctx_params, ed25519, edctx, 0); +} + +static void *ed448_newctx(void *vpctx, const char *propq) +{ + return sig_newctx(vpctx, propq, ed448_curves, ARRAY_SIZE(ed448_curves), + SIG_FMT_RAW, eddsa_set_ctx_params, ed448, edctx, 0); +} + +static void sig_freectx(void *vsctx) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + + if (!sctx) + return; + + zpc_ecdsa_ctx_free(&sctx->zpc_ctx); + EVP_MD_CTX_free(sctx->fwd_md_ctx); + OPENSSL_free(sctx->propq); + OPENSSL_free(sctx); +} + +/* dispatch prehashed sign/verify */ +static int sig_sign_init(void *vsctx, void *provkey, + const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_SIGN); +} + +static int sig_verify_init(void *vsctx, void *provkey, + const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_VERIFY); +} + +static int sig_sign(void *vsctx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *tbs, size_t tbslen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + size_t _siglen; + int rv; + + if (!sctx || !tbs || + !sctx->zpc_ctx || + sctx->op != SIG_OP_SIGN) + return OSSL_RV_ERR; + + _siglen = siglen ? *siglen : sigsize; + + rv = (sctx->fmt == SIG_FMT_RAW) ? + sig_sign_raw(sctx, sig, &_siglen, sigsize, tbs, tbslen) : + sig_sign_der(sctx, sig, &_siglen, sigsize, tbs, tbslen); + + if (rv == OSSL_RV_OK) + if (siglen) + *siglen = _siglen; + + return rv; +} + +static int sig_verify(void *vsctx, const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + + if (!sctx || !sig || !tbs || + !sctx->zpc_ctx || + sctx->op != SIG_OP_VERIFY) + return OSSL_RV_ERR; + + return (sctx->fmt == SIG_FMT_RAW) ? + sig_verify_raw(sctx, sig, siglen, tbs, tbslen) : + sig_verify_der(sctx, sig, siglen, tbs, tbslen); +} + +/* dispatch digest sign/verify */ +static int sig_digest_sign_init(void *vsctx, const char *mdname, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_digest_init(vsctx, mdname, provkey, params, SIG_OP_SIGN); +} + +static int sig_digest_verify_init(void *vsctx, const char *mdname, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_digest_init(vsctx, mdname, provkey, params, SIG_OP_VERIFY); +} + +static int sig_digest_update(void *vsctx, const unsigned char *data, + size_t datalen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + + if (!sctx) + return OSSL_RV_ERR; + + return EVP_DigestUpdate(sctx->fwd_md_ctx, data, datalen); +} + +static int sig_digest_sign_final(void *vsctx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + unsigned char tbs[EVP_MAX_MD_SIZE]; + unsigned int tbslen = EVP_MAX_MD_SIZE; + + if (!sctx) + return OSSL_RV_ERR; + + if (sig && + EVP_DigestFinal_ex(sctx->fwd_md_ctx, tbs, &tbslen) != OSSL_RV_OK) + return OSSL_RV_ERR; + + return sig_sign(sctx, sig, siglen, sigsize, tbs, tbslen); +} + +static int sig_digest_verify_final(void *vsctx, const unsigned char *sig, + size_t siglen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + unsigned char tbs[EVP_MAX_MD_SIZE]; + unsigned int tbslen = EVP_MAX_MD_SIZE; + + if (!sctx) + return OSSL_RV_ERR; + + if (EVP_DigestFinal_ex(sctx->fwd_md_ctx, tbs, &tbslen) != OSSL_RV_OK) + return OSSL_RV_ERR; + + return sig_verify(sctx, sig, siglen, tbs, tbslen); +} + +static int ed_digest_sign_init(void *vsctx, const char *mdname __unused, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_SIGN); +} + +static int ed_digest_verify_init(void *vsctx, const char *mdname __unused, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_VERIFY); +} + +/* dispatch get/set */ +static const OSSL_PARAM *ecdsa_gettable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ecdsa_get_ctx_params(void *vsctx, OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p) { + int type; + + if (!sctx->fwd_md_ctx) + return OSSL_RV_ERR; + + type = EVP_MD_CTX_get_type(sctx->fwd_md_ctx); + if (algid_ecdsa(type, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p) { + /* REVISIT deterministic nonce support */ + if (OSSL_PARAM_set_uint(p, 0) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE); + if (p) { + int dsz = EVP_MD_CTX_get_size(sctx->fwd_md_ctx); + + if (dsz <= 0 || (OSSL_PARAM_set_size_t(p, dsz) != OSSL_RV_OK)) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p) { + const char *d = EVP_MD_CTX_get0_name(sctx->fwd_md_ctx); + if (!d || + OSSL_PARAM_set_utf8_string(p, d) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *ecdsa_settable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ecdsa_set_ctx_params(void *vsctx, const OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + const OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p) { + unsigned int nonce_type; + + /* REVISIT deterministic nonce support */ + if ((OSSL_PARAM_get_uint(p, &nonce_type) != OSSL_RV_OK) || + (nonce_type != 0)) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p) { + const char *mdname = NULL; + + if (OSSL_PARAM_get_utf8_string_ptr(p, &mdname) != OSSL_RV_OK || + sig_sctx_set_md(sctx, mdname, params) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *eddsa_gettable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int eddsa_get_ctx_params(void *vsctx, OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p) { + if (algid_eddsa(sctx->ed_instance, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_INSTANCE); + if (p) { + if (OSSL_PARAM_set_utf8_string(p, sctx->ed_instance) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p) { + if (OSSL_PARAM_set_octet_string(p, sctx->ed_ctx, sctx->ed_ctxlen) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *eddsa_settable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int eddsa_set_ctx_params(void *vsctx, const OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + const OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_INSTANCE); + if (p) { + const char *edi = NULL; + + if (OSSL_PARAM_get_utf8_string_ptr(p, &edi) != OSSL_RV_OK || + OPENSSL_strcasecmp(sctx->ed_instance, edi) != 0) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p) { + const void *edc = NULL; + size_t edcl; + + if (OSSL_PARAM_get_octet_string_ptr(p, &edc, &edcl) != OSSL_RV_OK || + sctx->ed_ctxlen != edcl || + memcmp(sctx->ed_ctx, edc, edcl) != 0) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_DISPATCH ecdsa_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ecdsa_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, VERIFY_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_INIT, sig_digest_sign_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_UPDATE, sig_digest_update), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_FINAL, sig_digest_sign_final), + + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_INIT, sig_digest_verify_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_UPDATE, sig_digest_update), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_FINAL, sig_digest_verify_final), + + DISPATCH_DEFN(SIGNATURE, GETTABLE_CTX_PARAMS, ecdsa_gettable_ctx_params), + DISPATCH_DEFN(SIGNATURE, GET_CTX_PARAMS, ecdsa_get_ctx_params), + DISPATCH_DEFN(SIGNATURE, SETTABLE_CTX_PARAMS, ecdsa_settable_ctx_params), + DISPATCH_DEFN(SIGNATURE, SET_CTX_PARAMS, ecdsa_set_ctx_params), + + DISPATCH_END, +}; + +static const OSSL_DISPATCH ed25519_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ed25519_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_MESSAGE_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, VERIFY_MESSAGE_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_INIT, ed_digest_sign_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_INIT, ed_digest_verify_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, GETTABLE_CTX_PARAMS, eddsa_gettable_ctx_params), + DISPATCH_DEFN(SIGNATURE, GET_CTX_PARAMS, eddsa_get_ctx_params), + DISPATCH_DEFN(SIGNATURE, SETTABLE_CTX_PARAMS, eddsa_settable_ctx_params), + DISPATCH_DEFN(SIGNATURE, SET_CTX_PARAMS, eddsa_set_ctx_params), + + DISPATCH_END, +}; + +static const OSSL_DISPATCH ed448_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ed448_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_MESSAGE_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, VERIFY_MESSAGE_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_INIT, ed_digest_sign_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_INIT, ed_digest_verify_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, GETTABLE_CTX_PARAMS, eddsa_gettable_ctx_params), + DISPATCH_DEFN(SIGNATURE, GET_CTX_PARAMS, eddsa_get_ctx_params), + DISPATCH_DEFN(SIGNATURE, SETTABLE_CTX_PARAMS, eddsa_settable_ctx_params), + DISPATCH_DEFN(SIGNATURE, SET_CTX_PARAMS, eddsa_set_ctx_params), + + DISPATCH_END, +}; + +const OSSL_ALGORITHM signature_ops[] = { + ALGORITHM_DEFN(PROV_NAMES_ECDSA, PROV_PROP, ecdsa_functions, PROV_DESC_ECDSA), + ALGORITHM_DEFN(PROV_NAMES_ED25519, PROV_PROP, ed25519_functions, PROV_DESC_ED25519), + ALGORITHM_DEFN(PROV_NAMES_ED448, PROV_PROP, ed448_functions, PROV_DESC_ED448), + ALGORITHM_END, +}; diff --git a/src/signature.h b/src/signature.h new file mode 100644 index 00000000..fbeed32f --- /dev/null +++ b/src/signature.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _SIGNATURE_H +#define _SIGNATURE_H + +#include + +extern const OSSL_ALGORITHM signature_ops[]; + +#endif /* _SIGNATURE_H */ From 3a8f90074c50019457f81c2f85c3ff56b0056441 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 9 Mar 2026 12:40:43 -0400 Subject: [PATCH 22/39] cmake: Integrate signature algorithms Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + src/provider.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0cb7a255..ccd6c920 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,7 @@ set(ZPCPROVIDER_SOURCES src/store.c src/keymgmt.c src/algid.c + src/signature.c ) add_library( diff --git a/src/provider.c b/src/provider.c index f9fd6a2d..f96c3ae2 100644 --- a/src/provider.c +++ b/src/provider.c @@ -12,6 +12,7 @@ #include "provider.h" #include "store.h" #include "keymgmt.h" +#include "signature.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -282,6 +283,9 @@ static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, case OSSL_OP_KEYMGMT: ops = keymgmt_ops; break; + case OSSL_OP_SIGNATURE: + ops = signature_ops; + break; default: ops = NULL; goto out; From 58f9a12e036eca37606ff35d2b57c3e0d8a7e3e9 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 10 Mar 2026 11:50:29 +0100 Subject: [PATCH 23/39] provider: Add tls-property helpers Add the supported TLS properties of the hbkzpc provider. Signed-off-by: Holger Dengler --- src/tls.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/tls.h | 10 +++++++ 2 files changed, 88 insertions(+) create mode 100644 src/tls.c create mode 100644 src/tls.h diff --git a/src/tls.c b/src/tls.c new file mode 100644 index 00000000..adb4f3ad --- /dev/null +++ b/src/tls.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +// Derived from OpenSSL source +#include +#include +#include + +#include "provider.h" +#include "ossl.h" +#include "tls.h" + +/* taken from include/internal/tlsgroups.h */ +#define OSSL_TLS_GROUP_ID_secp256r1 0x0017 +#define OSSL_TLS_GROUP_ID_secp384r1 0x0018 +#define OSSL_TLS_GROUP_ID_secp521r1 0x0019 + +/* taken from providers/common/capabilities.c */ +#define TLS_GROUP_ENTRY(tlsname, realname, algorithm, idx) \ +{ \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME, \ + tlsname, sizeof(tlsname)), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL, \ + realname, sizeof(realname)), \ + OSSL_PARAM_utf8_string(OSSL_CAPABILITY_TLS_GROUP_ALG, \ + algorithm, sizeof(algorithm)), \ + OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_ID, \ + (unsigned int *)&group_list[idx].group_id), \ + OSSL_PARAM_uint(OSSL_CAPABILITY_TLS_GROUP_SECURITY_BITS, \ + (unsigned int *)&group_list[idx].secbits), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_TLS, \ + (unsigned int *)&group_list[idx].mintls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_TLS, \ + (unsigned int *)&group_list[idx].maxtls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MIN_DTLS, \ + (unsigned int *)&group_list[idx].mindtls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_MAX_DTLS, \ + (unsigned int *)&group_list[idx].maxdtls), \ + OSSL_PARAM_int(OSSL_CAPABILITY_TLS_GROUP_IS_KEM, \ + (unsigned int *)&group_list[idx].is_kem), \ + OSSL_PARAM_END \ +} + +typedef struct tls_group_constants_st { + unsigned int group_id; /* Group ID */ + unsigned int secbits; /* Bits of security */ + int mintls; /* Minimum TLS version, -1 unsupported */ + int maxtls; /* Maximum TLS version (or 0 for undefined) */ + int mindtls; /* Minimum DTLS version, -1 unsupported */ + int maxdtls; /* Maximum DTLS version (or 0 for undefined) */ + int is_kem; /* Indicates utility as KEM */ +} TLS_GROUP_CONSTANTS; + +static const TLS_GROUP_CONSTANTS group_list[] = { + [0] = { OSSL_TLS_GROUP_ID_secp256r1, 128, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, + [1] = { OSSL_TLS_GROUP_ID_secp384r1, 192, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, + [2] = { OSSL_TLS_GROUP_ID_secp521r1, 256, TLS1_VERSION, 0, DTLS1_VERSION, 0, 0 }, +}; + +static const OSSL_PARAM tls_group_list[][11] = { + TLS_GROUP_ENTRY("secp256r1", "prime256v1", "EC", 0), + TLS_GROUP_ENTRY("P-256", "prime256v1", "EC", 0), + TLS_GROUP_ENTRY("secp384r1", "secp384r1", "EC", 1), + TLS_GROUP_ENTRY("P-384", "secp384r1", "EC", 1), + TLS_GROUP_ENTRY("secp521r1", "secp521r1", "EC", 2), + TLS_GROUP_ENTRY("P-521", "secp521r1", "EC", 2), +}; + +int tls_group_capabilities(OSSL_CALLBACK *cb, void *arg) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(tls_group_list); i++) { + if (cb(tls_group_list[i], arg) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} diff --git a/src/tls.h b/src/tls.h new file mode 100644 index 00000000..3dd4c6da --- /dev/null +++ b/src/tls.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _TLS_H +#define _TLS_H + +#include + +int tls_group_capabilities(OSSL_CALLBACK *cb, void *arg); + +#endif /* _TLS_H */ From 819d144185d0bb5293bc0260d6232ee2d3305f5c Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 10 Mar 2026 11:51:08 +0100 Subject: [PATCH 24/39] cmake: Integrate tls-property helpers Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + src/provider.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ccd6c920..d68acdd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,7 @@ set(ZPCPROVIDER_SOURCES src/keymgmt.c src/algid.c src/signature.c + src/tls.c ) add_library( diff --git a/src/provider.c b/src/provider.c index f96c3ae2..7b2ddb53 100644 --- a/src/provider.c +++ b/src/provider.c @@ -13,6 +13,7 @@ #include "store.h" #include "keymgmt.h" #include "signature.h" +#include "tls.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -297,6 +298,17 @@ static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, return ops; } +static int prov_get_capabilities(void *vpctx __unused, const char *capability, + OSSL_CALLBACK *cb, void *arg) +{ + int rv = OSSL_RV_OK; + + if (OPENSSL_strcasecmp(capability, "TLS-GROUP") == 0) + rv = tls_group_capabilities(cb, arg); + + return rv; +} + static const OSSL_ITEM *prov_get_reason_strings(void *vpctx __unused) { return reason_strings; @@ -308,6 +320,7 @@ static const OSSL_DISPATCH provider_dispatch_table[] = { { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, FUNC(prov_gettable_params) }, { OSSL_FUNC_PROVIDER_GET_PARAMS, FUNC(prov_get_params) }, { OSSL_FUNC_PROVIDER_QUERY_OPERATION, FUNC(prov_query_operation) }, + { OSSL_FUNC_PROVIDER_GET_CAPABILITIES, FUNC(prov_get_capabilities) }, { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, FUNC(prov_get_reason_strings) }, { 0, NULL } #undef FUNC From 4e627202a8311cbdc48083a9f267f9ea934f80da Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 13 Mar 2026 17:01:36 +0100 Subject: [PATCH 25/39] asn1: Add ASN.1 module (definition and functions) The ASN.1 module provides DER en-/decoding for hbkzpc-URIs. These functions are required for the decoder/encoder support. Signed-off-by: Holger Dengler --- src/asn1.c | 21 +++++++++++++++++++++ src/asn1.h | 24 ++++++++++++++++++++++++ src/asn1_gen.c.in | 10 ++++++++++ 3 files changed, 55 insertions(+) create mode 100644 src/asn1.c create mode 100644 src/asn1.h create mode 100644 src/asn1_gen.c.in diff --git a/src/asn1.c b/src/asn1.c new file mode 100644 index 00000000..ca0749ff --- /dev/null +++ b/src/asn1.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include "asn1.h" + +ASN1_SEQUENCE(ZPCHBK) = { + ASN1_EXP(ZPCHBK, desc, ASN1_VISIBLESTRING, 0), + ASN1_EXP(ZPCHBK, uri, ASN1_UTF8STRING, 1), +} ASN1_SEQUENCE_END(ZPCHBK); + +#include "asn1_gen.c" + +int i2d_ZPCHBK_bio(BIO *bp, const ZPCHBK *hbkp) +{ + return ASN1_i2d_bio_of(ZPCHBK, i2d_ZPCHBK, bp, hbkp); +} + +ZPCHBK *d2i_ZPCHBK_bio(BIO *bp, ZPCHBK **hbkpp) +{ + return ASN1_d2i_bio_of(ZPCHBK, ZPCHBK_new, d2i_ZPCHBK, + bp, hbkpp); +} diff --git a/src/asn1.h b/src/asn1.h new file mode 100644 index 00000000..21e91b4b --- /dev/null +++ b/src/asn1.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _ASN1_H +#define _ASN1_H + +#include +#include + +#define HBKZPC_PEM_STRING "ZPC HARDWARE BACKED KEY" +#define HBKZPC_DER_DESC "HBKZPC Provider URI v1.0" + +struct zpc_hardware_backed_key_sequence_st { + ASN1_VISIBLESTRING *desc; + ASN1_UTF8STRING *uri; +}; +typedef struct zpc_hardware_backed_key_sequence_st ZPCHBK; +DECLARE_ASN1_FUNCTIONS(ZPCHBK) + +int i2d_ZPCHBK_bio(BIO *bp, const ZPCHBK *hbkp); +ZPCHBK *d2i_ZPCHBK_bio(BIO *bp, ZPCHBK **hbkpp); + +DECLARE_PEM_write_bio(ZPCHBK, ZPCHBK) +DECLARE_PEM_read_bio(ZPCHBK, ZPCHBK) +#endif /* _ASN1_H */ diff --git a/src/asn1_gen.c.in b/src/asn1_gen.c.in new file mode 100644 index 00000000..1f719c08 --- /dev/null +++ b/src/asn1_gen.c.in @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include + +BEGIN: +IMPLEMENT_ASN1_FUNCTIONS(ZPCHBK) +IMPLEMENT_PEM_write_bio(ZPCHBK, ZPCHBK, HBKZPC_PEM_STRING, ZPCHBK) +IMPLEMENT_PEM_read_bio(ZPCHBK, ZPCHBK, HBKZPC_PEM_STRING, ZPCHBK) From 2ee6a8ed13c8a03947e853791f2679ef2710e3c3 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 13 Mar 2026 17:05:25 +0100 Subject: [PATCH 26/39] cmake: Add ASN.1 build target Add internal object build target for ASN.1 module. The internal object can be shared between targets. Signed-off-by: Holger Dengler --- CMakeLists.txt | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d68acdd1..84e83508 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,52 @@ target_include_directories( ${OPENSSL_INCLUDE_DIR} ) +########################################################### +# ASN.1 + +add_custom_command( + OUTPUT asn1_gen.c + COMMAND ${CMAKE_C_COMPILER} -I${OPENSSL_INCLUDE_DIR} -x c -E ${CMAKE_SOURCE_DIR}/src/asn1_gen.c.in + | grep -v "^#" + | sed -n -e "1,/^BEGIN:$$/!p" + | clang-format --assume-filename=.c + > asn1_gen.c + MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/src/asn1_gen.c.in + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) + +add_custom_target( + asn1_gen + DEPENDS asn1_gen.c +) + +set (ASN1_SOURCES + src/asn1.c +) + +add_library( + asn1 OBJECT + ${ASN1_SOURCES} +) + +set_target_properties( + asn1 + PROPERTIES + POSITION_INDEPENDENT_CODE ON +) + +target_include_directories( + asn1 + PRIVATE + ${OPENSSL_INCLUDE_DIR} + ${CMAKE_BINARY_DIR} +) + +add_dependencies( + asn1 + asn1_gen +) + ########################################################### # zpcprovider @@ -186,6 +232,7 @@ target_link_libraries( OpenSSL::Crypto $ $ + $ ) target_link_options( From 608349d5f5a9a9f9b78493013f31c398bfc96eab Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sat, 14 Mar 2026 09:18:59 +0100 Subject: [PATCH 27/39] provider: Add decoders for hbkzpc-URI Add decoders for PEM and DER to support hbkzpc-URI files. Signed-off-by: Holger Dengler --- src/decoder.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/decoder.h | 10 +++ 2 files changed, 195 insertions(+) create mode 100644 src/decoder.c create mode 100644 src/decoder.h diff --git a/src/decoder.c b/src/decoder.c new file mode 100644 index 00000000..8d900fd0 --- /dev/null +++ b/src/decoder.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include + +#include "store_local.h" + +#include "provider.h" +#include "decoder.h" +#include "ossl.h" +#include "asn1.h" +#include "uri.h" +#include "map.h" + +#define DECODER_DER_STRUCTURE "zpchbk" +#define DECODER_PROP_PEM PROV_PROP",input=pem" +#define DECODER_PROP_DER PROV_PROP",input=der,structure="DECODER_DER_STRUCTURE + +#define DECODER_CARRYON OSSL_RV_TRUE +#define DECODER_STOP OSSL_RV_FALSE + +struct decoder_ctx { + struct provider_ctx *pctx; +}; + +static void *dec_newctx(void *vpctx) +{ + struct provider_ctx *pctx = (struct provider_ctx *)vpctx; + struct decoder_ctx *dctx; + + if (!pctx) + return NULL; + + dctx = OPENSSL_zalloc(sizeof(struct decoder_ctx)); + if (!dctx) + return NULL; + + dctx->pctx = pctx; + return dctx; +} + +static void dec_freectx(void *vdctx) +{ + OPENSSL_free(vdctx); +} + +static int dec_pem_der_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection __unused, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb __unused, + void *cbarg __unused) +{ + char *label = NULL, *header = NULL; + struct decoder_ctx *dctx = vdctx; + unsigned char *data = NULL; + int rc = DECODER_CARRYON; + OSSL_PARAM params[3]; + long datalen; + BIO *bin; + + bin = BIO_new_from_core_bio(dctx->pctx->libctx, in); + if (!bin) + goto out; + + if (PEM_read_bio(bin, &label, &header, &data, &datalen) != OSSL_RV_OK || + OPENSSL_strcasecmp(label, HBKZPC_PEM_STRING) != 0) + goto out; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, + data, datalen); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, + DECODER_DER_STRUCTURE, 0); + params[2] = OSSL_PARAM_construct_end(); + rc = data_cb(params, data_cbarg); +out: + OPENSSL_free(header); + OPENSSL_free(label); + OPENSSL_free(data); + BIO_free(bin); + + return rc; +} + +static int dec_der_decode(struct decoder_ctx *dctx, OSSL_CORE_BIO *in, + int selection, const char *data_type, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + struct parsed_uri *puri = NULL; + const char *uri_data_type; + bool public_only = false; + int rv = DECODER_CARRYON; + ZPCHBK *hbk = NULL; + BIO *bin; + + bin = BIO_new_from_core_bio(dctx->pctx->libctx, in); + if (!bin) + goto out; + + if (!d2i_ZPCHBK_bio(bin, &hbk)) + goto out; + + puri = parsed_uri_new((const char *)ASN1_STRING_get0_data(hbk->uri)); + if (!puri) + goto out; + + uri_data_type = alg2data_type(puri->origin_alg.value); + + if (OPENSSL_strcasecmp(data_type, uri_data_type) != 0) + goto out; + + public_only = selection && !(selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY); + + rv = store_load_uri(dctx->pctx, puri, public_only, + data_cb, data_cbarg, cb, cbarg); +out: + parsed_uri_free(puri); + OPENSSL_free(hbk); + BIO_free(bin); + return rv; +} + +static int dec_der_ec_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return dec_der_decode(vdctx, in, selection, PROV_NAME_EC, + data_cb, data_cbarg, cb, cbarg); +} + +static int dec_der_ed25519_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return dec_der_decode(vdctx, in, selection, PROV_NAME_ED25519, + data_cb, data_cbarg, cb, cbarg); +} + +static int dec_der_ed448_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return dec_der_decode(vdctx, in, selection, PROV_NAME_ED448, + data_cb, data_cbarg, cb, cbarg); +} + +static const OSSL_DISPATCH decoder_der_ec_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_der_ec_decode), + DISPATCH_END, +}; + +static const OSSL_DISPATCH decoder_der_ed25519_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_der_ed25519_decode), + DISPATCH_END, +}; + +static const OSSL_DISPATCH decoder_der_ed448_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_der_ed448_decode), + DISPATCH_END, +}; + +static const OSSL_DISPATCH decoder_pem_der_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_pem_der_decode), + DISPATCH_END, +}; + +const OSSL_ALGORITHM decoder_ops[] = { + ALGORITHM_DEFN("DER", DECODER_PROP_PEM, decoder_pem_der_functions, NULL), + ALGORITHM_DEFN(PROV_NAME_EC, DECODER_PROP_DER, decoder_der_ec_functions, NULL), + ALGORITHM_DEFN(PROV_NAME_ED25519, DECODER_PROP_DER, decoder_der_ed25519_functions, NULL), + ALGORITHM_DEFN(PROV_NAME_ED448, DECODER_PROP_DER, decoder_der_ed448_functions, NULL), + ALGORITHM_END, +}; diff --git a/src/decoder.h b/src/decoder.h new file mode 100644 index 00000000..3c400169 --- /dev/null +++ b/src/decoder.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _DECODER_H +#define _DECODER_H + +#include + +extern const OSSL_ALGORITHM decoder_ops[]; + +#endif /* _DECODER_H */ From 7d4447e6420e4a0c684478233652d203203e2da0 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sat, 14 Mar 2026 09:20:46 +0100 Subject: [PATCH 28/39] cmake: Integrate decoder implementation Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + src/provider.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 84e83508..86b66e5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,7 @@ set(ZPCPROVIDER_SOURCES src/algid.c src/signature.c src/tls.c + src/decoder.c ) add_library( diff --git a/src/provider.c b/src/provider.c index 7b2ddb53..9d4cdc36 100644 --- a/src/provider.c +++ b/src/provider.c @@ -14,6 +14,7 @@ #include "keymgmt.h" #include "signature.h" #include "tls.h" +#include "decoder.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -287,6 +288,9 @@ static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, case OSSL_OP_SIGNATURE: ops = signature_ops; break; + case OSSL_OP_DECODER: + ops = decoder_ops; + break; default: ops = NULL; goto out; From aa53a68e940e1ce60b3df7fa80b9b36341fc919f Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 19 Aug 2025 17:55:05 +0200 Subject: [PATCH 29/39] test: Add OpenSSL configuration template To use the zpc functionality via the OpenSSL API, the zpc provider has to be defined in the OpenSSL configuration. The build configures the template and creates a `openssl.cnf` file, which can be used for test purposes. The configuration file will be created in the build output folder. Signed-off-by: Holger Dengler --- CMakeLists.txt | 11 +++++++++++ openssl.cnf.in | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 openssl.cnf.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 86b66e5e..f1dbe52f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,17 @@ install( LIBRARY DESTINATION ${CMAKE_INSTALL_LIBEXECDIR} ) +set(OPENSSL_CONF + ${CMAKE_BINARY_DIR}/openssl.cnf +) +set(OPENSSL_CONF_IN + ${CMAKE_SOURCE_DIR}/openssl.cnf.in +) +set(ZPCPROVIDER_MODULE + ${CMAKE_BINARY_DIR}/zpcprovider.so +) +configure_file(${OPENSSL_CONF_IN} ${OPENSSL_CONF} @ONLY) + # install( # FILES ${CMAKE_SOURCE_DIR}/man/zpcprovider.cnf.5 # DESTINATION ${CMAKE_INSTALL_MANDIR}/man5 diff --git a/openssl.cnf.in b/openssl.cnf.in new file mode 100644 index 00000000..8d3edc09 --- /dev/null +++ b/openssl.cnf.in @@ -0,0 +1,28 @@ +HOME = . + +# Use this in order to automatically load providers. +openssl_conf = openssl_init + +config_diagnostics = 1 + +[openssl_init] +providers = provider_sect +alg_section = evp_properties + +[provider_sect] +default = default_sect +base = base_sect +hbkzpc = hbkzpc_sect + +[evp_properties] + +[base_sect] +activate = 1 + +[default_sect] +activate = 1 + +[hbkzpc_sect] +module = @ZPCPROVIDER_MODULE@ +identity = hbkzpc +activate = 1 From 7b374cdd34cdfd83bdcc17f9f0f3fbcc71f99887 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 3 Oct 2025 14:28:13 -0400 Subject: [PATCH 30/39] test: Add provider tests Add provider test framework with a base retrieval of the hbkzpc provider. Signed-off-by: Holger Dengler --- test/t_provider.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test/t_provider.c diff --git a/test/t_provider.c b/test/t_provider.c new file mode 100644 index 00000000..319d1488 --- /dev/null +++ b/test/t_provider.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include + +#include "ossl.h" + +#define PROVIDER "hbkzpc" +#define PROP_PROVIDER "provider=" PROVIDER + +enum rv { + PASS = 0, + SKIP = 77, + FAIL = 99, +}; + +static const char *stringify(int rv) +{ + switch(rv) { + case PASS: + return "PASS"; + case FAIL: + return "FAIL"; + case SKIP: + return "SKIP"; + }; + return "n/a"; +} + +static int test_provider(void) +{ + OSSL_LIB_CTX *libctx = NULL; + OSSL_PROVIDER *provider = NULL; + int rv = FAIL; + + provider = OSSL_PROVIDER_load(libctx, PROVIDER); + if (!provider) { + fprintf(stderr, "no provider %s\n", PROVIDER); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = PASS; +out: + return rv; +} + +#define RUNTEST(t) do { fprintf(stdout, "%s - %s\n", #t, stringify(t())); } while (0) + +int main(void) +{ + RUNTEST(test_provider); +} From aaf068277cb96724c978b4d92a8cd1d275662ab2 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 11 May 2026 09:11:12 +0200 Subject: [PATCH 31/39] cmake: Integrate provider test Integrate new provider testcases for builds with test enabled. Signed-off-by: Holger Dengler --- CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f1dbe52f..c4173519 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,6 +274,40 @@ configure_file(${OPENSSL_CONF_IN} ${OPENSSL_CONF} @ONLY) option(BUILD_TEST OFF) option(BUILD_INTERNAL_TEST OFF) +if (BUILD_TEST) + +enable_testing() + +set (ZPCPROVIDER_TEST_SOURCES + test/t_provider.c +) + +add_executable( + runprovidertest + ${ZPCPROVIDER_TEST_SOURCES} +) + +add_dependencies( + runprovidertest + zpcprovider +) + +target_include_directories( + runprovidertest + PRIVATE + src + ${OPENSSL_INCLUDE_DIR} +) + +target_link_libraries( + runprovidertest + PRIVATE + OpenSSL::Crypto + $ $ +) + +endif () # BUILD_TEST + if (BUILD_INTERNAL_TEST) enable_testing() From 74878499a0b410197309865bce2b164fc717360a Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Thu, 12 Feb 2026 16:18:56 +0100 Subject: [PATCH 32/39] test: Add provider test for store-loader Add tests for the sore-loader. It covers mainly the open()/load()/eof() sequence. Signed-off-by: Holger Dengler --- test/t_provider.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/t_provider.c b/test/t_provider.c index 319d1488..b946a35e 100644 --- a/test/t_provider.c +++ b/test/t_provider.c @@ -4,12 +4,22 @@ #include #include #include +#include #include "ossl.h" #define PROVIDER "hbkzpc" #define PROP_PROVIDER "provider=" PROVIDER +#define HBKZPC_URI_ECDSA_UV "hbkzpc:comment=uv-secret-name=ecdsa" \ + ";origin-alg=prime256v1" \ + ";origin-type=uv" \ + ";origin-blob=318f9f812b9e89ec3bd948c00afc751749a5a275b3a709436288da62f4cb36fb" \ + ";origin-pubkey=24873fcb06306adc8f65c8ecd5aaf7bc" \ + "6b62db8db50d8ce01677e49d48e3be50" \ + "af733b6e30a4c24fb52209cf6254b2b8" \ + "7c6ffb65a50583cea136a0488d431923" + enum rv { PASS = 0, SKIP = 77, @@ -47,9 +57,51 @@ static int test_provider(void) return rv; } +static int test_store_load_eof(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + OSSL_STORE_INFO *info = NULL; + OSSL_STORE_CTX *sctx; + int rv = FAIL; + + sctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (!sctx) { + fprintf(stderr, "fail: OSSL_STORE_open() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + if (OSSL_STORE_eof(sctx) != 0) { + fprintf(stderr, "fail: OSSL_STORE_eof() != 0 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + info = OSSL_STORE_load(sctx); + if (!info) { + fprintf(stderr, "fail: OSSL_STORE_load() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + if (OSSL_STORE_eof(sctx) != 1) { + fprintf(stderr, "fail: OSSL_STORE_eof() != 1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + rv = PASS; +out_close: + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(sctx); +out: + return rv; +} + #define RUNTEST(t) do { fprintf(stdout, "%s - %s\n", #t, stringify(t())); } while (0) int main(void) { RUNTEST(test_provider); + RUNTEST(test_store_load_eof); } From 006ea68bf644db8cca4206ba1df903025031ad81 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 17 Feb 2026 09:45:28 -0500 Subject: [PATCH 33/39] test: Add provider test for PKEY (store/keymgmt) Add test for loading a EVP PKEY object from a hbkzpc-URI via store and keymgmt. Signed-off-by: Holger Dengler --- test/t_provider.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/t_provider.c b/test/t_provider.c index b946a35e..1e33af93 100644 --- a/test/t_provider.c +++ b/test/t_provider.c @@ -98,10 +98,63 @@ static int test_store_load_eof(void) return rv; } +static int test_store_get_pkey(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + OSSL_STORE_CTX *sctx; + void *key = NULL; + int rv = FAIL; + + sctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (!sctx) { + fprintf(stderr, "fail: OSSL_STORE_open() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + while (!OSSL_STORE_eof(sctx)) { + OSSL_STORE_INFO *info = OSSL_STORE_load(sctx); + if (!info) { + fprintf(stderr, "fail: OSSL_STORE_load() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + switch (OSSL_STORE_INFO_get_type(info)) { + case OSSL_STORE_INFO_PUBKEY: + key = OSSL_STORE_INFO_get1_PUBKEY(info); + break; + case OSSL_STORE_INFO_PKEY: + key = OSSL_STORE_INFO_get1_PKEY(info); + break; + default: + OSSL_STORE_INFO_free(info); + continue; + } + + OSSL_STORE_INFO_free(info); + break; + } + + if (!key) { + fprintf(stderr, "fail: OSSL_STORE_INFO_[PUBKEY|PKEY] lookup [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + EVP_PKEY_free(key); + rv = PASS; +out_close: + OSSL_STORE_close(sctx); +out: + return rv; +} + #define RUNTEST(t) do { fprintf(stdout, "%s - %s\n", #t, stringify(t())); } while (0) int main(void) { RUNTEST(test_provider); RUNTEST(test_store_load_eof); + RUNTEST(test_store_get_pkey); } From 4515fff851a68097f4b033de6ebcc312c1fc4d30 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 17 Mar 2026 21:37:28 +0100 Subject: [PATCH 34/39] test: Add provider test for signature algorithms Add sign/verify tests for full and pre-hashed messages. The verification is checked across both variants. Note: The test covers only ECDSA and uses a hard-coded key origin. Signed-off-by: Holger Dengler --- test/t_provider.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) diff --git a/test/t_provider.c b/test/t_provider.c index 1e33af93..fee39db4 100644 --- a/test/t_provider.c +++ b/test/t_provider.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "ossl.h" @@ -26,6 +27,18 @@ enum rv { FAIL = 99, }; +static unsigned char text[32]; + +static unsigned char sha256[32]; + +static unsigned char *sig = NULL; +static size_t siglen = 0; + +static unsigned char *dgst_sig = NULL; +static size_t dgst_siglen = 0; + +/* helpers */ + static const char *stringify(int rv) { switch(rv) { @@ -39,6 +52,66 @@ static const char *stringify(int rv) return "n/a"; } +static EVP_PKEY *uri_pkey_get1(const char *uri) +{ + OSSL_STORE_CTX *sctx; + void *key = NULL; + + sctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (!sctx) { + fprintf(stderr, "fail: OSSL_STORE_open() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + while (!OSSL_STORE_eof(sctx)) { + OSSL_STORE_INFO *info = OSSL_STORE_load(sctx); + if (!info) { + fprintf(stderr, "fail: OSSL_STORE_load() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + switch (OSSL_STORE_INFO_get_type(info)) { + case OSSL_STORE_INFO_PKEY: + key = OSSL_STORE_INFO_get1_PKEY(info); + break; + default: + OSSL_STORE_INFO_free(info); + continue; + } + + OSSL_STORE_INFO_free(info); + break; + } +out_close: + OSSL_STORE_close(sctx); +out: + return key; +} + +static int sha2_256(unsigned char *in, size_t inlen) +{ + size_t len = sizeof(sha256); + + return EVP_Q_digest(NULL, "SHA2-256", NULL, + in, inlen, + sha256, &len); +} + +static void reset_globals(void) +{ + OPENSSL_free(sig); + sig = NULL; + siglen = 0; + + OPENSSL_free(dgst_sig); + dgst_sig = NULL; + dgst_siglen = 0; +} + +/* tests */ + static int test_provider(void) { OSSL_LIB_CTX *libctx = NULL; @@ -150,11 +223,282 @@ static int test_store_get_pkey(void) return rv; } +static int test_signature_sign(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *signing_key; + EVP_PKEY_CTX *ctx; + int rv = FAIL; + + signing_key = uri_pkey_get1(uri); + if (!signing_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + ctx = EVP_PKEY_CTX_new(signing_key, NULL); + if (ctx == NULL) { + fprintf(stderr, "fail: EVP_PKEY_CTX_new [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto pkey_out; + } + + if (EVP_PKEY_sign_init(ctx) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_sign_init [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + /* Determine buffer length */ + if (EVP_PKEY_sign(ctx, NULL, &siglen, sha256, sizeof(sha256)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_sign [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + sig = OPENSSL_malloc(siglen); + if (sig == NULL) { + fprintf(stderr, "fail: malloc [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + if (EVP_PKEY_sign(ctx, sig, &siglen, sha256, sizeof(sha256)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_sign [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + OPENSSL_free(sig); + sig = NULL; + goto ctx_out; + } + + rv = PASS; +ctx_out: + EVP_PKEY_CTX_free(ctx); +pkey_out: + EVP_PKEY_free(signing_key); +out: + return rv; +} + +static int _test_signature_verify(unsigned char *s, size_t slen) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *verify_key; + EVP_PKEY_CTX *ctx; + int rv = FAIL; + + if (!sig || !siglen) + return SKIP; + + verify_key = uri_pkey_get1(uri); + if (!verify_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + ctx = EVP_PKEY_CTX_new(verify_key, NULL); + if (ctx == NULL) { + fprintf(stderr, "fail: EVP_PKEY_CTX_new [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto pkey_out; + } + + if (EVP_PKEY_verify_init(ctx) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_verify_init [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + if (EVP_PKEY_verify(ctx, s, slen, sha256, sizeof(sha256)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_verify [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + OPENSSL_free(s); + s = NULL; + goto ctx_out; + } + + rv = PASS; +ctx_out: + EVP_PKEY_CTX_free(ctx); +pkey_out: + EVP_PKEY_free(verify_key); +out: + return rv; +} + +static int test_signature_verify(void) +{ + return _test_signature_verify(sig, siglen); +} + +static int test_signature_cross_verify(void) +{ + return _test_signature_verify(dgst_sig, dgst_siglen); +} + +static int _test_signature_digest_sign(EVP_PKEY *signing_key, + const char *msg) +{ + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY_CTX *ctx = NULL; + int rv = FAIL; + + /* just to make sure... */ + if (!signing_key) + return SKIP; + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + fprintf(stderr, "fail: EVP_MD_CTX_new [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + if (EVP_DigestSignInit_ex(md_ctx, &ctx, "SHA256", NULL, NULL, signing_key, NULL) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestSignInit_ex [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + if (EVP_DigestSignUpdate(md_ctx, text, sizeof(text)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestSignUpdate [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + /* Determine buffer length */ + if (EVP_DigestSignFinal(md_ctx, NULL, &dgst_siglen) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestSignFinal [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + dgst_sig = OPENSSL_malloc(dgst_siglen); + if (dgst_sig == NULL) { + fprintf(stderr, "fail: malloc [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + if (EVP_DigestSignFinal(md_ctx, dgst_sig, &dgst_siglen) != OSSL_RV_OK) { + fprintf(stderr, "fail: DigestSignFinal [%s]\n", msg); + ERR_print_errors_fp(stderr); + OPENSSL_free(dgst_sig); + dgst_sig = NULL; + goto out; + } + + rv = PASS; +out: + EVP_MD_CTX_free(md_ctx); + return rv; +} + +static int test_signature_digest_sign_uri(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *signing_key = NULL; + int rv = SKIP; + + signing_key = uri_pkey_get1(uri); + if (!signing_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = _test_signature_digest_sign(signing_key, "uri=" HBKZPC_URI_ECDSA_UV); +out: + EVP_PKEY_free(signing_key); + return rv; +} + +static int _test_signature_digest_verify(unsigned char *t, size_t tlen, + unsigned char *s, size_t slen) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *verify_key; + EVP_MD_CTX *md_ctx; + EVP_PKEY_CTX *ctx; + int rv = FAIL; + + if (!s || !slen) + return SKIP; + + verify_key = uri_pkey_get1(uri); + if (!verify_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + fprintf(stderr, "fail: EVP_MD_CTX_new [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto pkey_out; + } + + if (EVP_DigestVerifyInit_ex(md_ctx, &ctx, "SHA256", NULL, NULL, verify_key, NULL) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestVerifyInit_ex [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto md_out; + } + + if (EVP_DigestVerifyUpdate(md_ctx, t, tlen) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestVerifyUpdate [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto md_out; + } + + if (EVP_DigestVerifyFinal(md_ctx, s, slen) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestVerifyFinal [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + s = NULL; + goto md_out; + } + + rv = PASS; +md_out: + EVP_MD_CTX_free(md_ctx); +pkey_out: + EVP_PKEY_free(verify_key); +out: + return rv; +} + +static int test_signature_digest_verify(void) +{ + return _test_signature_digest_verify(text, sizeof(text), dgst_sig, dgst_siglen); +} + +static int test_signature_cross_digest_verify(void) +{ + return _test_signature_digest_verify(text, sizeof(text), sig, siglen); +} + + #define RUNTEST(t) do { fprintf(stdout, "%s - %s\n", #t, stringify(t())); } while (0) int main(void) { + RAND_bytes(text, sizeof(text)); + sha2_256(text, sizeof(text)); + RUNTEST(test_provider); RUNTEST(test_store_load_eof); RUNTEST(test_store_get_pkey); + RUNTEST(test_signature_sign); + RUNTEST(test_signature_verify); + RUNTEST(test_signature_digest_sign_uri); + RUNTEST(test_signature_digest_verify); + RUNTEST(test_signature_cross_verify); + RUNTEST(test_signature_cross_digest_verify); + + reset_globals(); } From e314843c96a936636fa837239612afe1f687b976 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 18 Mar 2026 13:54:23 +0100 Subject: [PATCH 35/39] test: Add asn.1 tests Add tests for the DER encoding. It takes the hard-coded ECDSA key and stores it as a file. Signed-off-by: Holger Dengler --- test/t_provider.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/t_provider.c b/test/t_provider.c index fee39db4..22c6a774 100644 --- a/test/t_provider.c +++ b/test/t_provider.c @@ -1,13 +1,16 @@ // SPDX-License-Identifier: MIT // Copyright contributors to the libzpc project +#include #include #include +#include #include #include #include #include #include "ossl.h" +#include "asn1.h" #define PROVIDER "hbkzpc" #define PROP_PROVIDER "provider=" PROVIDER @@ -21,6 +24,12 @@ "af733b6e30a4c24fb52209cf6254b2b8" \ "7c6ffb65a50583cea136a0488d431923" +#define HBKZPC_FILE_ECDSA_DER "ecdsa.der" +#define HBKZPC_FILE_ECDSA_PEM "ecdsa.pem" + +static const char *default_file_der = HBKZPC_FILE_ECDSA_DER; +static const char *default_file_pem = HBKZPC_FILE_ECDSA_PEM; + enum rv { PASS = 0, SKIP = 77, @@ -37,6 +46,9 @@ static size_t siglen = 0; static unsigned char *dgst_sig = NULL; static size_t dgst_siglen = 0; +static const char *file_der = NULL; +static const char *file_pem = NULL; + /* helpers */ static const char *stringify(int rv) @@ -99,6 +111,49 @@ static int sha2_256(unsigned char *in, size_t inlen) sha256, &len); } +static int uri2file(const char *uri, const char *path, bool der) +{ + int rv = OSSL_RV_ERR; + ZPCHBK *zpchbk = NULL; + BIO *bo = NULL; + + if (!uri || !path) + goto out; + + zpchbk = ZPCHBK_new(); + if (!zpchbk) { + fprintf(stderr, "Unable to create hbk\n"); + goto out; + } + + ASN1_STRING_set(zpchbk->desc, HBKZPC_DER_DESC, -1); + ASN1_STRING_set(zpchbk->uri, uri, -1); + + bo = BIO_new_file(path, "w"); + if (!bo) { + fprintf(stderr, "Unable to open file: %s\n", path); + goto out; + } + + if (der) { + if (!i2d_ZPCHBK_bio(bo, zpchbk)) { + fprintf(stderr, "Unable to write DER file %s\n", path); + goto out; + } + } else { + if (!PEM_write_bio_ZPCHBK(bo, zpchbk)) { + fprintf(stderr, "Unable to write PEM file %s\n", path); + goto out; + } + } + + rv = OSSL_RV_OK; +out: + ZPCHBK_free(zpchbk); + BIO_free(bo); + return rv; +} + static void reset_globals(void) { OPENSSL_free(sig); @@ -482,6 +537,31 @@ static int test_signature_cross_digest_verify(void) return _test_signature_digest_verify(text, sizeof(text), sig, siglen); } +static int test_uri_der_file(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + const char *file = default_file_der; + int rv = FAIL; + + rv = uri2file(uri, file, true) == OSSL_RV_OK ? PASS : FAIL; + if (rv == PASS) + file_der = file; + + return rv; +} + +static int test_uri_pem_file(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + const char *file = default_file_pem; + int rv = OSSL_RV_ERR; + + rv = uri2file(uri, file, false) == OSSL_RV_OK ? PASS : FAIL; + if (rv == PASS) + file_pem = file; + + return rv; +} #define RUNTEST(t) do { fprintf(stdout, "%s - %s\n", #t, stringify(t())); } while (0) @@ -501,4 +581,7 @@ int main(void) RUNTEST(test_signature_cross_digest_verify); reset_globals(); + + RUNTEST(test_uri_der_file); + RUNTEST(test_uri_pem_file); } From 511058928f9061bae3082a93f7ce4bc54393b35c Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 24 Mar 2026 15:38:27 +0100 Subject: [PATCH 36/39] test: Add decoder tests Add simple decoder fetch test. Signed-off-by: Holger Dengler --- test/t_provider.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/t_provider.c b/test/t_provider.c index 22c6a774..afeeaa67 100644 --- a/test/t_provider.c +++ b/test/t_provider.c @@ -563,6 +563,34 @@ static int test_uri_pem_file(void) return rv; } +static int test_pem_pkey(void) +{ + EVP_PKEY *signing_key = NULL; + BIO *bin = NULL; + int rv = FAIL; + + bin = BIO_new_file(default_file_pem, "r"); + if (!bin) { + fprintf(stderr, "fail: BIO_new_file [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + signing_key = PEM_read_bio_PrivateKey(bin, &signing_key, NULL, ""); + if (!signing_key) { + fprintf(stderr, "fail: PEM_read_bio_PrivateKey [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = PASS; + EVP_PKEY_free(signing_key); +out: + BIO_free(bin); + return rv; +} + #define RUNTEST(t) do { fprintf(stdout, "%s - %s\n", #t, stringify(t())); } while (0) int main(void) @@ -584,4 +612,5 @@ int main(void) RUNTEST(test_uri_der_file); RUNTEST(test_uri_pem_file); + RUNTEST(test_pem_pkey); } From 27c8ed2c185a19849f7ad8c1d08ff84f3804fa10 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 8 Apr 2026 13:55:34 +0200 Subject: [PATCH 37/39] test: Add signature test (PEM) Add tests to use hbkzpc-URI file for sign/verify (instead of loading the URI via store). Signed-off-by: Holger Dengler --- test/t_provider.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/t_provider.c b/test/t_provider.c index afeeaa67..897c70e7 100644 --- a/test/t_provider.c +++ b/test/t_provider.c @@ -472,6 +472,33 @@ static int test_signature_digest_sign_uri(void) return rv; } +static int test_signature_digest_sign_pem(void) +{ + EVP_PKEY *signing_key = NULL; + BIO *bin = NULL; + int rv = SKIP; + + bin = BIO_new_file(default_file_pem, "r"); + if (!bin) { + fprintf(stderr, "fail: BIO_new_file [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + goto out; + } + + signing_key = PEM_read_bio_PrivateKey(bin, &signing_key, NULL, ""); + if (!signing_key) { + fprintf(stderr, "fail: PEM_read_bio_PrivateKey [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = _test_signature_digest_sign(signing_key, "path=" HBKZPC_FILE_ECDSA_PEM); +out: + EVP_PKEY_free(signing_key); + BIO_free(bin); + return rv; +} + static int _test_signature_digest_verify(unsigned char *t, size_t tlen, unsigned char *s, size_t slen) { @@ -613,4 +640,8 @@ int main(void) RUNTEST(test_uri_der_file); RUNTEST(test_uri_pem_file); RUNTEST(test_pem_pkey); + RUNTEST(test_signature_digest_sign_pem); + RUNTEST(test_signature_digest_verify); + + reset_globals(); } From 4b970237590dffc574846a90506bb4cefa5837bc Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 8 May 2026 14:42:48 -0400 Subject: [PATCH 38/39] test: provider: Add ed25519 PKEY test case Signed-off-by: Holger Dengler --- test/t_provider.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/t_provider.c b/test/t_provider.c index 897c70e7..a2d2e015 100644 --- a/test/t_provider.c +++ b/test/t_provider.c @@ -26,6 +26,7 @@ #define HBKZPC_FILE_ECDSA_DER "ecdsa.der" #define HBKZPC_FILE_ECDSA_PEM "ecdsa.pem" +#define HBKZPC_FILE_ED25519_PEM "ed25519.pem" static const char *default_file_der = HBKZPC_FILE_ECDSA_DER; static const char *default_file_pem = HBKZPC_FILE_ECDSA_PEM; @@ -644,4 +645,7 @@ int main(void) RUNTEST(test_signature_digest_verify); reset_globals(); + + default_file_pem = HBKZPC_FILE_ED25519_PEM; + RUNTEST(test_pem_pkey); } From d281dfe6e626030fc35c4c11003b2a9851b146d1 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 07:49:18 -0500 Subject: [PATCH 39/39] WIP dbg: Add provider gdb-scripts Signed-off-by: Holger Dengler --- misc/dbg/decoder.gdb | 7 +++++++ misc/dbg/kmgmt.gdb | 17 +++++++++++++++++ misc/dbg/provider.gdb | 8 ++++++++ misc/dbg/signature.gdb | 22 ++++++++++++++++++++++ misc/dbg/store.gdb | 11 +++++++++++ 5 files changed, 65 insertions(+) create mode 100644 misc/dbg/decoder.gdb create mode 100644 misc/dbg/kmgmt.gdb create mode 100644 misc/dbg/provider.gdb create mode 100644 misc/dbg/signature.gdb create mode 100644 misc/dbg/store.gdb diff --git a/misc/dbg/decoder.gdb b/misc/dbg/decoder.gdb new file mode 100644 index 00000000..c2290046 --- /dev/null +++ b/misc/dbg/decoder.gdb @@ -0,0 +1,7 @@ +set breakpoint pending on +break dec_newctx +break dec_freectx +break dec_pem_der_decode +break dec_der_ec_decode +break dec_der_ed25519_decode +break dec_der_ed448_decode diff --git a/misc/dbg/kmgmt.gdb b/misc/dbg/kmgmt.gdb new file mode 100644 index 00000000..615539a1 --- /dev/null +++ b/misc/dbg/kmgmt.gdb @@ -0,0 +1,17 @@ +set breakpoint pending on +break kmgmt_new +break kmgmt_free +break kmgmt_load +break kmgmt_has +break kmgmt_export_types_ex +break kmgmt_set_params +break kmgmt_settable_params +break ec_get_params +break ec_export +break ed_get_params +break ed_export +break ec_gettable_params +break ed_gettable_params +break ec_query_operation_name +break ed25519_query_operation_name +break ed448_query_operation_name diff --git a/misc/dbg/provider.gdb b/misc/dbg/provider.gdb new file mode 100644 index 00000000..a59aa49e --- /dev/null +++ b/misc/dbg/provider.gdb @@ -0,0 +1,8 @@ +set breakpoint pending on +break prov_init +break prov_teardown +break prov_gettable_params +break prov_get_params +break prov_query_operation +break prov_get_capabilities +break prov_get_reason_strings diff --git a/misc/dbg/signature.gdb b/misc/dbg/signature.gdb new file mode 100644 index 00000000..efd55b46 --- /dev/null +++ b/misc/dbg/signature.gdb @@ -0,0 +1,22 @@ +set breakpoint pending on +break sig_digest_sign_final +break sig_digest_sign_init +break sig_digest_update +break sig_digest_verify_final +break sig_digest_verify_init +break sig_freectx +break sig_sign +break sig_sign_init +break sig_verify +break sig_verify_init +break ecdsa_get_ctx_params +break ecdsa_gettable_ctx_params +break ecdsa_set_ctx_params +break ecdsa_settable_ctx_params +break eddsa_get_ctx_params +break eddsa_gettable_ctx_params +break eddsa_set_ctx_params +break eddsa_settable_ctx_params +break ecdsa_newctx +break ed25519_newctx +break ed448_newctx diff --git a/misc/dbg/store.gdb b/misc/dbg/store.gdb new file mode 100644 index 00000000..bf37da9c --- /dev/null +++ b/misc/dbg/store.gdb @@ -0,0 +1,11 @@ +set breakpoint pending on +break store_ctx_init +break store_ctx_free +break store_ctx_expect +break store_open +break store_open_ex +break store_load +break store_eof +break store_close +break store_set_ctx_params +break store_settable_ctx_params