From 75e381849fa7dcad01344e0474427833c2c54507 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Thu, 21 May 2026 10:46:11 +0200 Subject: [PATCH 01/34] zpc: ecc_key: Add key compare API The new zpc_ec_key_compare() function compares two key objects. In general, if both keys has the same public key, they can be assumed to be identical. Only if one of the two keys (or both) has not set a public key, also the protected key blobs needs to be compared. Signed-off-by: Holger Dengler --- include/zpc/ecc_key.h | 10 ++++++++++ include/zpc/error.h | 6 ++++++ src/ecc_key.c | 43 ++++++++++++++++++++++++++++++++++++++++++- src/error.c | 1 + 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/include/zpc/ecc_key.h b/include/zpc/ecc_key.h index 301ff725..6d8e1b7f 100644 --- a/include/zpc/ecc_key.h +++ b/include/zpc/ecc_key.h @@ -210,6 +210,16 @@ int zpc_ec_key_reencipher(struct zpc_ec_key *key, unsigned int reenc); __attribute__((visibility("default"))) void zpc_ec_key_free(struct zpc_ec_key **key); +/** + * Compare two EC key objects. The key objects may be updated on deep + * compare (no public key data availabe). + * \param[in/out] key1 EC key + * \param[in/out] key2 EC key + * \return 0 on success. Otherwise a non-zero error code is returned. + */ +__attribute__((visibility("default"))) +int zpc_ec_key_compare(struct zpc_ec_key *key1, struct zpc_ec_key *key2); + # ifdef __cplusplus /* *INDENT-OFF* */ } diff --git a/include/zpc/error.h b/include/zpc/error.h index 050f8fd2..2bb49e10 100644 --- a/include/zpc/error.h +++ b/include/zpc/error.h @@ -523,6 +523,12 @@ extern "C" { */ # define ZPC_ERROR_XTS_KEYGEN_VIA_SYSFS 86 +/** + * \def ZPC_ERROR_EC_KEY_MISMATCH + * \brief EC key compare mismatch. + */ +# define ZPC_ERROR_EC_KEY_MISMATCH 87 + /** * \fn const char *zpc_error_string(int err) * \brief Map an error code to the corresponding error string. diff --git a/src/ecc_key.c b/src/ecc_key.c index f2bb08bb..5052ccdf 100644 --- a/src/ecc_key.c +++ b/src/ecc_key.c @@ -5,7 +5,6 @@ * it under the terms of the MIT license. See LICENSE for details. */ -#include #include #include "zpc/ecc_key.h" @@ -1221,6 +1220,48 @@ void zpc_ec_key_free(struct zpc_ec_key **ec_key) DEBUG("return"); } +int zpc_ec_key_compare(struct zpc_ec_key *ec_key1, struct zpc_ec_key *ec_key2) +{ + int rc; + + if (!ec_key1) { + rc = ZPC_ERROR_ARG1NULL; + goto ret; + } + + if (!ec_key2) { + rc = ZPC_ERROR_ARG2NULL; + goto ret; + } + + /* public key compare */ + if (ec_key1->pubkey_set && ec_key2->pubkey_set && + ec_key1->pub.publen == ec_key2->pub.publen && + memcmp(ec_key1->pub.pubkey, ec_key2->pub.pubkey, + ec_key1->pub.publen) == 0) { + rc = 0; + goto ret; + } + + /* re-derive both keys */ + if ((rc = ec_key_sec2prot(ec_key1, EC_KEY_SEC_CUR)) || + (rc = ec_key_sec2prot(ec_key2, EC_KEY_SEC_CUR))) + return rc; + + /* protected key compare */ + if (ec_key1->key_set && ec_key2->key_set && + memcmp_consttime(ec_key1->prot.protkey, ec_key2->prot.protkey, + MAXECPROTKEYSIZE) == 0) { + rc = 0; + goto ret; + } + + rc = ZPC_ERROR_EC_KEY_MISMATCH; +ret: + DEBUG("return %d (%s)", rc, zpc_error_string(rc)); + return rc; +} + /* * Reset everything that was set after allocation. * Caller must hold ec_key's wr lock. diff --git a/src/error.c b/src/error.c index cf963be7..51f08b9a 100644 --- a/src/error.c +++ b/src/error.c @@ -104,6 +104,7 @@ zpc_error_string(int err) "HMAC key generation via sysfs attributes failed.", "Creating a block-sized HMAC key failed.", "Creating a full-xts key via sysfs attributes failed", + "EC key compare mismatch", "LAST" }; const char *rc; From a2ab62eeef10946e4e39f90377c73ca98d693897 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 1 Jun 2026 12:17:10 +0200 Subject: [PATCH 02/34] zpc: init: Introduce explicit constructor/destructor The libzpc API is no longer exposed as shared object, so make the constructor and destructor explicit. It is now up to the consumer to call them. Signed-off-by: Holger Dengler --- CMakeLists.txt | 1 + include/zpc/init.h | 9 +++++++++ src/globals.c | 7 +++---- 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 include/zpc/init.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ffb978..ce273d32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,7 @@ project(${ZPC_NAME} ) set(ZPC_HEADERS + include/zpc/init.h include/zpc/error.h include/zpc/aes_key.h include/zpc/aes_xts_key.h diff --git a/include/zpc/init.h b/include/zpc/init.h new file mode 100644 index 00000000..928c4ead --- /dev/null +++ b/include/zpc/init.h @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _INIT_H +#define _INIT_H + +void zpc_init(void); +void zpc_fini(void); + +#endif /* _INIT_H */ diff --git a/src/globals.c b/src/globals.c index 779c4804..87972864 100644 --- a/src/globals.c +++ b/src/globals.c @@ -10,6 +10,7 @@ #include "globals.h" #include "misc.h" #include "debug.h" +#include "zpc/init.h" #include #include @@ -106,8 +107,7 @@ static const int init = 0; static const int init = 1; #endif -__attribute__((constructor)) -static void zpc_init(void) +void zpc_init(void) { unsigned long hwcap, facility_list_nmemb; u64 status_word[2], *facility_list = NULL, tmp; @@ -439,8 +439,7 @@ static void zpc_init(void) return; } -__attribute__((destructor)) -static void zpc_fini(void) +void zpc_fini(void) { int rc; From 8cffd8183f2c59cd457f2c4e10100276f231bf2c Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 21 Apr 2026 16:47:09 +0200 Subject: [PATCH 03/34] travis: Add OpenSSL custom build Update travis configuration to noble and add install the new package dependencies (e.g. OpenSSL). While at it, also remove deprecated statements and remove environment variable settings. Signed-off-by: Holger Dengler --- .travis.yml | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index b38836db..ca2f97d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,17 @@ arch: s390x os: linux -dist: jammy -language: cpp -sudo: required -env: | - CFLAGS="-O3 -Wextra -Wextra -Werror" - CXXFLAGS="-O3 -Wextra -Wextra -Werror" +dist: noble +language: c + compiler: - gcc - clang before_install: - sudo apt-get update -qq - - sudo apt-get install cmake libjson-c-dev + - sudo apt-get install -y cmake pandoc clang-format libjson-c-dev libssl-dev 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 . 2> >(tee) + - cmake --build build 2> >(tee) From f36999ec8d549398bd47f693fc6844a71732fb4c Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 4 May 2026 16:08:56 +0200 Subject: [PATCH 04/34] 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 ce273d32..ee14522e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,11 +147,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 @@ -356,8 +356,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 20bed2a1444dac025bdd2f39698091fbe26137b5 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 10:07:36 +0200 Subject: [PATCH 05/34] 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 ee14522e..30fb56b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -104,17 +104,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( @@ -123,21 +117,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 @@ -376,7 +357,7 @@ set(ZPC_TEST_LIBS ${GTEST_LIB_DIR}/libgtest.a Threads::Threads json-c::json-c - zpc + $ ) set(ZPC_TEST_SOURCES From 4e4f5b312ee7e39649e370f25ed43c7313cffbe1 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 11 May 2026 09:04:30 +0200 Subject: [PATCH 06/34] 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 30fb56b4..bb0017ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,8 +123,9 @@ include(GNUInstallDirs) # Test option(BUILD_TEST OFF) +option(BUILD_INTERNAL_TEST OFF) -if (BUILD_TEST) +if (BUILD_INTERNAL_TEST) enable_testing() @@ -405,7 +406,7 @@ target_include_directories(runtest PRIVATE include src ${GTEST_INCLUDE_DIR}) include(GoogleTest) gtest_discover_tests(runtest) -endif () +endif () # BUILD_INTERNAL_TEST ########################################################### # doc From 2ffb9d476d8b47dd40927c12632caf78cac9d895 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 07:35:20 +0200 Subject: [PATCH 07/34] 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 bb0017ce..4c870715 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,7 @@ add_definitions( ) set(ZPC_LIBS - Threads::Threads ${CMAKE_DL_LIBS} + Threads::Threads ${CMAKE_DL_LIBS} ) add_library( @@ -208,19 +208,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) @@ -238,11 +238,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) @@ -260,11 +260,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) @@ -310,20 +310,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) @@ -340,10 +340,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 807291119b6d0bf7b5ffa77ffd71ae7da31b84e0 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sun, 17 May 2026 15:04:03 +0200 Subject: [PATCH 08/34] cmake: Add man-page conversion target The new target converts markdown man-pages to troff format. Signed-off-by: Holger Dengler --- CMakeLists.txt | 14 ++++++++++++++ conv_man.sh | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100755 conv_man.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c870715..0c25ed0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -436,3 +436,17 @@ add_custom_target(doc endif () endif () + +########################################################### +# man + +find_program(PANDOC pandoc) + +if (PANDOC) + +add_custom_target( + man ALL + COMMAND ${CMAKE_SOURCE_DIR}/conv_man.sh "${CMAKE_SOURCE_DIR}/man" "${CMAKE_BINARY_DIR}" +) + +endif () # PANDOC_FOUND diff --git a/conv_man.sh b/conv_man.sh new file mode 100755 index 00000000..992e1477 --- /dev/null +++ b/conv_man.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# Copyright contributors to the libzpc project + +SRCDIR=${1} +DSTDIR=${2} + +[ -z "${SRCDIR}" ] || [ -z "${DSTDIR}" ] && exit 1 +command -v pandoc >/dev/null 2>&1 || exit 1 + +for MD in "${SRCDIR}"/*.md; do + [ -r "${MD}" ] || continue + MAN=$(basename "${MD}" .md) + pandoc \ + --standalone \ + --to man \ + --out "${DSTDIR}/${MAN}" \ + "${MD}" +done +exit 0 From 420f0c021c256f043c5e3c1377a30f628cd48572 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 20:11:16 +0200 Subject: [PATCH 09/34] 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 0c25ed0c..6d6ab462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,11 @@ find_package(json-c REQUIRED ) +find_package(OpenSSL + 3.0.7 + REQUIRED +) + add_definitions( -D_GNU_SOURCE ) From eba230dad5d865261d6998a21d207c1ef97a89b6 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 May 2026 21:30:03 +0200 Subject: [PATCH 10/34] cmake: Introduce build option for address sanitizer Introduce the build option BUILD_ASAN to enable the address sanitizer in the compile options. Executable or shared library targets has to enable the address sanitizer in the link options as well. Signed-off-by: Holger Dengler --- CMakeLists.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d6ab462..6b0b0b64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,20 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS_DEBUG "-Wall -Wextra -m64 -g -O0") +option(BUILD_ASAN OFF) + +if (BUILD_ASAN) +string(APPEND + CMAKE_C_FLAGS_DEBUG + " -fsanitize=undefined -fsanitize=address" +) + +string(APPEND + CMAKE_CXX_FLAGS_DEBUG + " -fsanitize=undefined -fsanitize=address" +) +endif () # BUILD_ASAN + project(${ZPC_NAME} VERSION ${ZPC_VERSION_MAJOR}.${ZPC_VERSION_MINOR}.${ZPC_VERSION_PATCH} DESCRIPTION ${ZPC_DESCRIPTION} From 297415b5846a909cc4c174b035ea84669b914793 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 16:42:12 +0200 Subject: [PATCH 11/34] 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 --- man/hbkzpcprovider.7.md | 176 +++++++++++++++++ man/hbkzpcprovider.conf.5.md | 127 ++++++++++++ src/ossl.h | 22 +++ src/provider.c | 360 +++++++++++++++++++++++++++++++++++ src/provider.h | 65 +++++++ 5 files changed, 750 insertions(+) create mode 100644 man/hbkzpcprovider.7.md create mode 100644 man/hbkzpcprovider.conf.5.md create mode 100644 src/ossl.h create mode 100644 src/provider.c create mode 100644 src/provider.h diff --git a/man/hbkzpcprovider.7.md b/man/hbkzpcprovider.7.md new file mode 100644 index 00000000..825f28cd --- /dev/null +++ b/man/hbkzpcprovider.7.md @@ -0,0 +1,176 @@ +% HBKZPCPROVIDER(7) LIBZPC v2 +% +% 2026 + +# NAME + +`hbkzpc provider` - An OpenSSL provider that provides an interface to the +hardware-backed key cryptographic operations, available on IBM Z and IBM +LinuxONE. + +# DESCRIPTION + +IBM Z and IBM LinuxONE systems offer several types of hardware assists with +different features, including the CP Assist for Cryptographic Functions +(*CPACF*) and the IBM Crypto Express (*CEX*) features. + +The `hbkzpc provider` enables applications using the OpenSSL crypto library API +to exploit the protected key cryptography (*ZPC*) functions, provided by CPACF +on IBM Z and IBM LinuxONE. + +To use the `hbkzpc provider` via the OpenSSL crypto library API, it must be +referenced in the OpenSSL configuration. For more details, see +hbkzpcprovider.conf(5). + +## Hardware-backed keys + +Hardware-backed keys (*HBK*) are a generic concept, where the secret information +of cryptographic keys is never exposed to the main memory, but cryptographic +operations with such keys are still possible. Instead of the raw secret key +information, opaque key objects are exposed to the applications. The internals +of these opaque key objects (key slot reference, wrapped key blob, etc.) depends +on the concrete implementation of the generic concept. It is essential, that the +knowledge of the opaque key object does not expose any information about the +real secret information of the key. + +## IBM Z protected key cryptography + +The IBM Z protected key cryptography is the implementation of the generic +hardware-backed key concept on IBM Z and IBM LinuxONE. The platform uses IBM +Crypto Express (*CEX*) adapters (or often called: feature) or the IBM Secure +Execution for Linux ultravisor to securely store secret key material and +securely export them to the firmware. The `hbkzpc provider` can handle opaque +key objects (protected key origins), which describe and reference these secure +keys. + +ZPC provides many cryptographic functions with protected keys. The `hbkzpc +provider` supports ECDSA/EdDSA sign/verify operations for a list of +ECC-curves. See section *Provider functions* for more details. + +## Protected key origins + +A protected key origin for the `hbkzpc provider` is used to internally export +the referenced secure key to the firmware. While the resulting protected keys +are volatile and can become invalid at any time, the protected key origins are +persistent and can be stored without exposing any secret information. + +A protected key origin is specified by a HBKZPC unified resource identifier +(URI). These URIs can be encoded to protected key origin files. The `hbkzpc +provider` can handle either URIs directly or URIs which are encoded and stored +in files (DER or PEM). See section *URI* for more details. + +The `zpckey` tool supports composing such protected key origins. See zpckey(1) +for more details. + +The `hbkzpc provider` supports protected key origins for retrievable secrets in +IBM Secure Execution for Linux (SEL) ultravisor. + +## Provider functions + +The `hbkzpc provider` plugs into the OpenSSL provider API and provides the +following functions: + +- signature-algorithms for ECDSA and EdDSA +- key-manager for provider-specific keys +- store-loader for provider-specific key URIs +- decoder for DER/PEM encoded provider-specific key URI files + +## Supported ECC curves + +The `hbkzpc provider` supports the following ECC-curves for ECDSA and EdDSA +respectively: + +- prime256v1 +- secp384r1 +- secp521r1 +- ED25519 +- ED448 + +# URI + +The HBKZPC unified resource identifier (URI) specifies all information of the +protected key origins, which is required for the provider to derive the related +protected key. The URI has the following syntax (ABNF representation): + + hbkzpc-URI = "hbkzpc:" hbkzpc-param + hbkzpc-param = [ hbkzpc-pattr *(";" hbkzpc-pattr) ] + + hbkzpc-pattr = origin-type / origin-alg / origin-blob / + origin-pubkey / comment + + origin-type = "origin-type" "=" *pchar + origin-alg = "origin-alg" "=" oid / *(pchar) + origin-blob = "origin-blob" "=" hex-string + origin-pubkey = "origin-pubkey" "=" hex-string + comment = "comment" "=" *pchar-pct + + oid = DIGIT *("." DIGIT) + + hex-byte = 2*HEXDIG + hex-string = [ hex-byte *(hex-byte) ] + + pchar-pct = pchar / pct-encoded + pchar = unreserved / res-avail + res-avail = ":" / "[" / "]" / "@" / "!" / "$" / + "'" / "(" / ")" / "*" / "+" / "," / "=" + + pct-encoded = [ "%" 2*HEXDIG ] + unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + + +# ENCODING + +The URI can also be encoded and stored to a file. Supported encoding formats are +DER and PEM. + +## DER encoding + +For the DER encoding, the HBKZPC URI is encapsulated with a ASN.1 +structure. Each DER file contains one structure in binary form. The structure +has the following syntax: + + HardwareBackedKeyZPCFormat DEFINITIONS ::= BEGIN + HBKZPC ::= SEQUENCE { + desc [0] EXPLICIT VisibleString, + uri [1] EXPLICIT UTF8String, + } + END + +The DER decoder of the `hbkzpc provider` only handles DER files with a +description (*desc*) of *HBKZPC Provider URI v1.0*. + +## PEM encoding + +The PEM encoding is a base64-encoded DER structure. The encoded data is +surrounded by a start and end tag. A PEM encoded file has the following form: + + -----BEGIN HARDWARE BACKED KEY ZPC----- + [... ...] + -----END HARDWARE BACKED KEY ZPC----- + +The PEM decoder of the `hbkzpc provider` only handles PEM files with the label +*HARDWARE BACKED KEY ZPC*. + +# GLOSSARY + +CEX +: IBM Crypto Express + +CPACF +: CP Assist for Cryptographic Functions + +HBK +: hardware-backed key + +SEL +: IBM Secure Execution for Linux + +URI +: unified resource identifier + +ZPC +: IBM Z protected key cryptography + +# SEE ALSO + +zpckey(1), hbkzpcprovider.conf(5), provider(7SSL). diff --git a/man/hbkzpcprovider.conf.5.md b/man/hbkzpcprovider.conf.5.md new file mode 100644 index 00000000..38620093 --- /dev/null +++ b/man/hbkzpcprovider.conf.5.md @@ -0,0 +1,127 @@ +% HBKZPCPROVIDER.CNF(5) LIBZPC v2 +% +% 2026 + +# NAME + +hbkzpcprovider.conf - Configuration syntax for the `hbkzpc provider` + +# DESCRIPTION + +This page documents the syntax of the OpenSSL configuration file for the `hbkzpc +provider`. It is a sub-set of the OpenSSL configuration file format, described +in config(5SSL). + +# CONFIGURATION + +## OpenSSL Configuration + +The `hbkzpc provider` can be configured application-specific or system-wide. In +both cases, the configuration file needs to define and reference a section for +the `hbkzpc provider`, following the OpenSSL configuration syntax +(config(5SSL)). + +The provider section for the `hbkzpc provider` specifies the shared library of +the provider (mandatory) and an activation flag (optional). + +Since OpenSSL configuration supports drop-ins (keyword `.inlcude`), the +configuration for the `hbkzpc provider` can also be placed in a separate +configuration file. + +## Provider Section + +A provider section in the OpenSSL configuration defines generic parameters, as +well as provider-specific parameters. Each provider section must be referenced +in the global providers sections (*provider_sect*) of the OpenSSL configuration +file. The `hbkzpc provider` supports no provider-specific parameters, but the +provider section for the `hbkzpc provider` must specify at least the generic +provider section parameter *module*. For more details about the generic provider +parameters, see config(5SSL). + +module (mandatory) +: This parameter takes a path to the provider shared object file. For the + `hbkzpc provider`, use the absolute path to the installation location of + `zpcprovider.so` or the relative path to the OpenSSL modules directory. + +activate (optional) +: If present and set to `1`, then the associated provider is + activated. Conversely, setting this value to `0` or not specifying the + parameter at all prevents the provider from being activated (default: + `0`). + +The *identity* parameter may be used in the provider section, but it has no +impact to the registration of the `hbkzpc provider`. It will always register +itself with "provider=hbkzpc". Multiple instances of this provider may work but +are not supported. + +# EXAMPLES + +The following drop-in example specifies the `hbkzpc provider` module and +activates it. + + [provider_sect] + hbkzpc = hbkzpc_sect + + [hbkzpc_sect] + module = zpcprovider.so + activate = 1 + + +The drop-in file needs to be included in the main OpenSSL configuration, either +by specifying the drop-in file or the drop-in directory (for example +`/etc/pki/tls/openssl.d/` on Fedora). + + .include /path/to/drop-in-directory + +The following configuration example covers a complete minimal OpenSSL +configuration. It can be used for example as a temporary replacement for the +system-wide OpenSSL configuration. + + HOME = . + + openssl_conf = openssl_init + + [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 = /path/to/zpcprovider.so + activate = 1 + +To use this configuration example for an application, store it to a file +(e.g. my-openssl.conf) and reference this file in the environment variable +`OPENSSL_CONF`. The following command lists all configured providers: base, +default and hbkzpc. + + $ env OPENSSL_CONF=my-openssl.conf openssl list -providers + Providers: + base + name: OpenSSL Base Provider + version: 4.0.0 + status: active + default + name: OpenSSL Default Provider + version: 4.0.0 + status: active + hbkzpc + name: hbkzpc + version: 2.0.0 + status: active + +# SEE ALSO + +hbkzpcprovider(7), config(5SSL). 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..d99a8671 --- /dev/null +++ b/src/provider.c @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#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") }, + { ZPC_ERROR_EC_KEY_MISMATCH, + C("EC key compare mismatch") }, + { 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) +{ + struct provider_ctx *pctx = (struct provider_ctx *)vpctx; + + if (pctx) + OSSL_LIB_CTX_free(pctx->libctx); + 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: + prov_teardown(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..f2e05e96 --- /dev/null +++ b/src/provider.h @@ -0,0 +1,65 @@ +// 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_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 e9ddfa4cf1f50c421496734aa0260682a23d442c Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 20:11:48 +0200 Subject: [PATCH 12/34] cmake: Add zpcprovider build target Add a module build target for the zpcprovider. Other than shared objects, the provider module has no so-name and also no API versioning. Signed-off-by: Holger Dengler --- CMakeLists.txt | 96 ++++++++++++++++++++++++++++++++++++++++++++----- zpcprovider.map | 6 ++++ 2 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 zpcprovider.map diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b0b0b64..250af677 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,11 @@ -########################################################### -set(ZPC_NAME "libzpc" ) -set(ZPC_DESCRIPTION "IBM Z Protected-key Crypto library") -set(ZPC_VERSION_MAJOR 1 ) -set(ZPC_VERSION_MINOR 5 ) -set(ZPC_VERSION_PATCH 0 ) -########################################################### +############################################################################### +set(ZPC_NAME "libzpc" ) +set(ZPC_DESCRIPTION "IBM Z Protected-key Crypto library" ) +set(ZPC_VERSION_MAJOR 1 ) +set(ZPC_VERSION_MINOR 5 ) +set(ZPC_VERSION_PATCH 0 ) +set(ZPC_VERSION ${ZPC_VERSION_MAJOR}.${ZPC_VERSION_MINOR}.${ZPC_VERSION_PATCH}) +############################################################################### cmake_minimum_required( VERSION 3.10 @@ -45,7 +46,7 @@ string(APPEND endif () # BUILD_ASAN project(${ZPC_NAME} - VERSION ${ZPC_VERSION_MAJOR}.${ZPC_VERSION_MINOR}.${ZPC_VERSION_PATCH} + VERSION ${ZPC_VERSION} DESCRIPTION ${ZPC_DESCRIPTION} LANGUAGES CXX C ASM ) @@ -115,6 +116,10 @@ find_package(OpenSSL REQUIRED ) +find_package(PkgConfig + QUIET +) + add_definitions( -D_GNU_SOURCE ) @@ -138,6 +143,81 @@ 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_compile_definitions( + zpcprovider + PRIVATE + PROV_VERSION="${ZPC_VERSION}" +) + +target_link_libraries( + zpcprovider + PRIVATE + OpenSSL::Crypto +) + +target_link_options( + zpcprovider + PRIVATE + -Wl,--version-script=${CMAKE_SOURCE_DIR}/zpcprovider.map +) + +if (BUILD_ASAN) +target_link_options( + zpcprovider + BEFORE PUBLIC -fsanitize=undefined + PUBLIC -fsanitize=address +) +endif () # BUILD_ASAN + +if(PkgConfig_FOUND) + pkg_get_variable(OSSL_MODULESDIR libcrypto modulesdir) +else() + set(OSSL_MODULESDIR ${CMAKE_INSTALL_LIBEXECDIR}) +endif() +message(STATUS "OpenSSL module install directory: ${OSSL_MODULESDIR}") + +install( + TARGETS zpcprovider + LIBRARY + DESTINATION ${OSSL_MODULESDIR} +) + +install( + FILES ${CMAKE_BINARY_DIR}/hbkzpcprovider.conf.5 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man5 +) + +install( + FILES ${CMAKE_BINARY_DIR}/hbkzpcprovider.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 863e022baf552ef2bc7876cb9887cfcf40a8c66e Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 14:07:00 +0100 Subject: [PATCH 13/34] 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 | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/object.h | 41 +++++++++++++++++++ 2 files changed, 151 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..2c5a55f0 --- /dev/null +++ b/src/object.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#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); +} + +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); +} + +struct obj *obj_dup(const struct obj *osrc) +{ + struct obj *odst; + + if (!osrc || + !(odst = obj_new(osrc->pctx))) + return NULL; + + if (osrc->origin_type && + !(odst->origin_type = OPENSSL_strdup(osrc->origin_type))) + goto err; + + if (osrc->origin_alg && + !(odst->origin_alg = OPENSSL_strdup(osrc->origin_alg))) + goto err; + + if (osrc->apqns && + !(odst->apqns = OPENSSL_strdup(osrc->apqns))) + goto err; + + if (osrc->mkvp && + !(odst->mkvp = OPENSSL_strdup(osrc->mkvp))) + goto err; + + if (osrc->origin_blob.p && + !(odst->origin_blob.p = OPENSSL_memdup(osrc->origin_blob.p, + osrc->origin_blob.plen))) + goto err; + odst->origin_blob.plen = osrc->origin_blob.plen; + + if (osrc->origin_pubkey.p && + !(odst->origin_pubkey.p = OPENSSL_memdup(osrc->origin_pubkey.p, + osrc->origin_pubkey.plen))) + goto err; + odst->origin_pubkey.plen = osrc->origin_pubkey.plen; + + return odst; +err: + obj_free(odst); + return NULL; +} + +bool obj_cmp(const struct obj *obj1, const struct obj *obj2) +{ + if ((!!obj1 != !!obj2) || + (OPENSSL_strcasecmp(obj1->origin_alg, obj2->origin_alg) != 0)) + return false; + + if ((!!obj1->origin_pubkey.p == !!obj2->origin_pubkey.p) && + (obj1->origin_pubkey.plen == obj2->origin_pubkey.plen)) + return (memcmp(obj1->origin_blob.p, + obj2->origin_blob.p, + obj1->origin_blob.plen) == 0) + ? true + : false; + + return false; +} diff --git a/src/object.h b/src/object.h new file mode 100644 index 00000000..b0de1686 --- /dev/null +++ b/src/object.h @@ -0,0 +1,41 @@ +// 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; + + /* 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); +struct obj *obj_dup(const struct obj *obj); +void obj_free(struct obj *obj); +bool obj_cmp(const struct obj *obj1, const struct obj *obj2); + +#endif /* _OBJECT_H */ From 6cdf4efa78bf9d62491e0e97c244375cb386db09 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 14:36:47 +0100 Subject: [PATCH 14/34] 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 250af677..37cf77b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,7 @@ include(GNUInstallDirs) set(ZPCPROVIDER_SOURCES src/provider.c + src/object.c ) add_library( From 5e4a4a5fdd135135f202dea3d43b447c29d04bdb Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:53:09 +0100 Subject: [PATCH 15/34] 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 | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/uri.h | 36 +++++++++ 2 files changed, 256 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..9f30692b --- /dev/null +++ b/src/uri.c @@ -0,0 +1,220 @@ +// 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}; + + 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++; + } +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 1bf69285a90d13143cb3082f8e639460920978c1 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:54:30 +0100 Subject: [PATCH 16/34] 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 37cf77b8..57e9f0fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -143,6 +143,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 @@ -180,6 +204,7 @@ target_link_libraries( zpcprovider PRIVATE OpenSSL::Crypto + $ ) target_link_options( From 9284e38ebcd78aa0dee776e18957d83247db71b7 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 03:21:27 -0500 Subject: [PATCH 17/34] 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 82f0260f6deec0ec0add9f17aecf47e1228344d9 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 03:22:34 -0500 Subject: [PATCH 18/34] 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 57e9f0fe..63260917 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,7 @@ target_include_directories( set(ZPCPROVIDER_SOURCES src/provider.c src/object.c + src/map.c ) add_library( From 8165a0df5f8ef221a492f3ed6f9d595e177197d9 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 20 Apr 2026 11:23:25 +0200 Subject: [PATCH 19/34] 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 | 333 ++++++++++++++++++++++++++++++++++++++++++++++ src/store.h | 10 ++ src/store_local.h | 17 +++ 3 files changed, 360 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..3423aa39 --- /dev/null +++ b/src/store.c @@ -0,0 +1,333 @@ +// 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); + data->p = NULL; + data->plen = 0; + 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 93b56db502a066818351a6c7bbfa8c22399d369e Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:59:57 +0100 Subject: [PATCH 20/34] 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 63260917..a9e5ea6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,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 d99a8671..d720a265 100644 --- a/src/provider.c +++ b/src/provider.c @@ -11,6 +11,7 @@ #include "ossl.h" #include "provider.h" +#include "store.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -281,6 +282,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 322f03b57a3169b6fc1d8e47ecaba281e0b84537 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 18 Feb 2026 04:37:32 -0500 Subject: [PATCH 21/34] 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 Signed-off-by: Holger Dengler --- src/keymgmt.c | 710 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/keymgmt.h | 10 + src/object.c | 3 + src/object.h | 2 + 4 files changed, 725 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..7c6c33eb --- /dev/null +++ b/src/keymgmt.c @@ -0,0 +1,710 @@ +// 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/ecdsa_ctx.h" +#include "zpc/error.h" + +#define ASN1_SIG_HDR 8 + +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 + ASN1_SIG_HDR, + }, { + .alg = SN_secp384r1, + .bits = 384, .secbits = 192, .sigsz = 96 + ASN1_SIG_HDR, + }, { + .alg = SN_secp521r1, + .bits = 521, .secbits = 256, .sigsz = 132 + ASN1_SIG_HDR, + }, { + .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; +} + +#define HASHSZ 32 +#define SIGSZ 140 +static int zpc_key_check(const struct obj *obj) +{ + const unsigned char hash[HASHSZ] = { 0 }; + struct zpc_ecdsa_ctx *ctx = NULL; + unsigned char sig[SIGSZ]; + size_t siglen = SIGSZ; + int rc, rv = OSSL_RV_ERR; + + if (!obj->ec_key || !obj->origin_pubkey.p) + return OSSL_RV_OK; + + if ((rc = zpc_ecdsa_ctx_alloc(&ctx)) || + (rc = zpc_ecdsa_ctx_set_key(ctx, obj->ec_key)) || + (rc = zpc_ecdsa_sign(ctx, hash, HASHSZ, sig, &siglen)) || + (rc = zpc_ecdsa_verify(ctx, hash, HASHSZ, sig, siglen))) { + PROV_ERR_raise(obj->pctx, rc); + goto out; + } + + rv = OSSL_RV_OK; +out: + zpc_ecdsa_ctx_free(&ctx); + return rv; +} +#undef HASHLEN +#undef SIGLEN + +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; + else + OPENSSL_free(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; + OSSL_PARAM_BLD *param_bld = NULL; + unsigned char *pub, *p = NULL; + 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); + OPENSSL_free(p); + return rv; +} + +#define DECL_KMGMT_FN(tname, name) DECL_DISPATCH_FUNC(keymgmt, tname, name) +DECL_KMGMT_FN(new, kmgmt_new); +DECL_KMGMT_FN(dup, kmgmt_dup); +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, kmgmt_export_types); +#ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX +DECL_KMGMT_FN(export_types_ex, kmgmt_export_types_ex); +#endif +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_dup(const void *keydata_from, int selection) +{ + const struct obj *obj_src = (struct obj *)keydata_from; + struct obj *obj_dst = NULL; + + if (!obj_src || + !(obj_dst = obj_dup(obj_src))) + return NULL; + + obj_dst->public_only = selection && !(selection & + OSSL_KEYMGMT_SELECT_PRIVATE_KEY); + if (obj_src->public_only && !obj_dst->public_only) + goto err; + + if (zpc_key_update(obj_dst) != OSSL_RV_OK) + goto err; + + return obj_dst; +err: + obj_free(obj_dst); + return NULL; +} + +static void kmgmt_free(void *keydata) +{ + obj_free((struct obj *)keydata); +} + +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_OK || + zpc_key_check(obj) != OSSL_RV_OK) + 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 kmgmt_match(const void *keydata1, const void *keydata2, + int selection __unused) +{ + const struct obj *obj1 = (struct obj *)keydata1; + const struct obj *obj2 = (struct obj *)keydata2; + int rc; + + if (!obj1 || !obj2) + return OSSL_RV_FALSE; + + if (obj_cmp(obj1, obj2)) + goto match; + + if ((rc = zpc_ec_key_compare(obj1->ec_key, obj2->ec_key))) { + PROV_ERR_raise(obj1->pctx, rc); + return OSSL_RV_FALSE; + } +match: + return OSSL_RV_TRUE; +} + +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(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]; +} + +#ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX +const OSSL_PARAM *kmgmt_export_types_ex(void *provctx __unused, int selection) +{ + return kmgmt_export_types(selection); +} +#endif + +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, DUP, kmgmt_dup), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, MATCH, kmgmt_match), + DISPATCH_DEFN(KEYMGMT, EXPORT, ec_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES, kmgmt_export_types), +#ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), +#endif + 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, DUP, kmgmt_dup), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, MATCH, kmgmt_match), + DISPATCH_DEFN(KEYMGMT, EXPORT, ed_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES, kmgmt_export_types), +#ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), +#endif + 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, DUP, kmgmt_dup), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, MATCH, kmgmt_match), + DISPATCH_DEFN(KEYMGMT, EXPORT, ed_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES, kmgmt_export_types), +#ifdef OSSL_FUNC_KEYMGMT_EXPORT_TYPES_EX + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), +#endif + 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 2c5a55f0..6b22188a 100644 --- a/src/object.c +++ b/src/object.c @@ -4,9 +4,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 b0de1686..56cdea1b 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; @@ -18,6 +19,7 @@ struct obj { struct provider_ctx *pctx; /* zpc keys */ + struct zpc_ec_key *ec_key; /* origin path attrs */ char *origin_type; From 21232c9f4b035ec67a6f2106f8a051c21eddac88 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 10:08:31 +0200 Subject: [PATCH 22/34] cmake: Integrate asymmetric key management Signed-off-by: Holger Dengler --- CMakeLists.txt | 2 ++ src/provider.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index a9e5ea6b..cc208451 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,6 +175,7 @@ set(ZPCPROVIDER_SOURCES src/object.c src/map.c src/store.c + src/keymgmt.c ) add_library( @@ -207,6 +208,7 @@ target_link_libraries( PRIVATE OpenSSL::Crypto $ + $ ) target_link_options( diff --git a/src/provider.c b/src/provider.c index d720a265..a8e5c1c9 100644 --- a/src/provider.c +++ b/src/provider.c @@ -8,10 +8,12 @@ #include #include +#include #include "ossl.h" #include "provider.h" #include "store.h" +#include "keymgmt.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -239,6 +241,8 @@ static void prov_teardown(void *vpctx) if (pctx) OSSL_LIB_CTX_free(pctx->libctx); OPENSSL_free(vpctx); + + zpc_fini(); } static const OSSL_PARAM *prov_gettable_params(void *vpctx __unused) @@ -285,6 +289,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; @@ -360,5 +367,6 @@ int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, const OSSL_DISPATCH **out, void **provctx) { + zpc_init(); return prov_init(handle, in, out, provctx); } From e146cbc4c360fabce06054234b38b701b5f6eb35 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 27 Apr 2026 16:53:47 +0200 Subject: [PATCH 23/34] 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 5faf933a7d66fb3d746ad0438ad1b6947e09b7f2 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 27 Apr 2026 16:54:36 +0200 Subject: [PATCH 24/34] 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 cc208451..d665ce35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,7 @@ set(ZPCPROVIDER_SOURCES src/map.c src/store.c src/keymgmt.c + src/algid.c ) add_library( From 74dafe0b8154d7b2e0a9af099547f5cca0519482 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 9 Mar 2026 12:37:49 -0400 Subject: [PATCH 25/34] provider: Add signature algorithms Add signature algorithms for sign/verify with ECDSA and EDDSA keys. Signed-off-by: Holger Dengler --- src/signature.c | 917 ++++++++++++++++++++++++++++++++++++++++++++++++ src/signature.h | 10 + 2 files changed, 927 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..8e705088 --- /dev/null +++ b/src/signature.c @@ -0,0 +1,917 @@ +// 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 +#define MAX_RAW_SIGSZ 256 + +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; + + struct obj *obj; + 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 = NULL; + 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; + + if (propq && + !(pq = OPENSSL_strdup(propq))) + 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; + EVP_MD *md = NULL; + + 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)) { + EVP_MD_free(md); + EVP_MD_CTX_free(md_ctx); + return OSSL_RV_ERR; + } + } + + EVP_MD_CTX_free(sctx->fwd_md_ctx); + sctx->fwd_md_ctx = md_ctx; + + EVP_MD_free(md); + 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; + } + } + + obj_free(sctx->obj); + sctx->obj = obj_get(obj); + + 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) +{ + int rc; + + *siglen = sigsize; + if ((rc = zpc_ecdsa_sign(sctx->zpc_ctx, tbs, tbslen, sig, siglen))) + PROV_ERR_raise(sctx->pctx, rc); + + return rc ? 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[MAX_RAW_SIGSZ]; + unsigned char *_sig; + size_t rawlen; + int rv; + + rawlen = MIN(sigsize, MAX_RAW_SIGSZ); + _sig = sig ? raw : NULL; + + rv = sig_sign_raw(sctx, _sig, &rawlen, rawlen, tbs, tbslen); + if (rv != OSSL_RV_OK) + return rv; + + if (!sig) { + if (siglen) + *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) +{ + int rc; + + if (!sctx || + !sctx->zpc_ctx || + sctx->op != SIG_OP_VERIFY) + return OSSL_RV_ERR; + + if ((rc = zpc_ecdsa_verify(sctx->zpc_ctx, tbs, tbslen, sig, siglen))) + PROV_ERR_raise(sctx->pctx, rc); + + return rc ? 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[MAX_RAW_SIGSZ]; + 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); +DISP_SIG(newctx, ed25519_newctx); +DISP_SIG(newctx, ed448_newctx); +DISP_SIG(dupctx, sig_dupctx); +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_init, ed_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_init, ed_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; + + obj_free(sctx->obj); + zpc_ecdsa_ctx_free(&sctx->zpc_ctx); + EVP_MD_CTX_free(sctx->fwd_md_ctx); + OPENSSL_free(sctx->propq); + OPENSSL_free(sctx); +} + +#if !OPENSSL_VERSION_PREREQ(3, 1) +static EVP_MD_CTX *EVP_MD_CTX_dup(const EVP_MD_CTX *in) +{ + EVP_MD_CTX *out = EVP_MD_CTX_new(); + + if (out != NULL && !EVP_MD_CTX_copy_ex(out, in)) { + EVP_MD_CTX_free(out); + out = NULL; + } + return out; +} +#endif + +static void *sig_dupctx(void *vsctx) +{ + struct sig_ctx *sctx_src = (struct sig_ctx *)vsctx; + struct sig_ctx *sctx_dst = NULL; + int rc; + + if (!sctx_src || + !(sctx_dst = sig_newctx(sctx_src->pctx, sctx_src->propq, + sctx_src->curves, sctx_src->curves_len, + sctx_src->fmt, sctx_src->set_ctx_params, + sctx_src->ed_instance, sctx_src->ed_ctx, + sctx_src->ed_ctxlen))) + return NULL; + + if (sctx_src->fwd_md_ctx && + !(sctx_dst->fwd_md_ctx = EVP_MD_CTX_dup(sctx_src->fwd_md_ctx))) + goto err; + + if (sctx_src->obj && + (rc = zpc_ecdsa_ctx_set_key(sctx_dst->zpc_ctx, + sctx_src->obj->ec_key))) { + PROV_ERR_raise(sctx_dst->pctx, rc); + goto err; + } + + sctx_dst->obj = obj_get(sctx_src->obj); + sctx_dst->op = sctx_src->op; + + return sctx_dst; +err: + sig_freectx(sctx_dst); + return NULL; +} + +/* 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), +#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, 0), +#endif + 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; + } + +#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE + 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; + } +#endif + + 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), +#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, 0), +#endif + 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; + +#ifdef OSSL_SIGNATURE_PARAM_NONCE_TYPE + 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; + } +#endif + + 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), +#ifdef OSSL_SIGNATURE_PARAM_INSTANCE + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), +#endif +#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), +#endif + 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; + } + +#ifdef OSSL_SIGNATURE_PARAM_INSTANCE + 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; + } +#endif + +#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING + 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; + } +#endif + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *eddsa_settable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { +#ifdef OSSL_SIGNATURE_PARAM_INSTANCE + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), +#endif +#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), +#endif + 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; + +#ifdef OSSL_SIGNATURE_PARAM_INSTANCE + 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; + } +#endif + +#ifdef OSSL_SIGNATURE_PARAM_CONTEXT_STRING + 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; + } +#endif + + return OSSL_RV_OK; +} + +#ifdef OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES +static const char **ecdsa_query_key_types(void) +{ + static const char *keytypes[] = { PROV_NAME_EC, NULL }; + return keytypes; +} + +static const char **ed25519_query_key_types(void) +{ + static const char *keytypes[] = { PROV_NAME_ED25519, NULL }; + return keytypes; +} + +static const char **ed448_query_key_types(void) +{ + static const char *keytypes[] = { PROV_NAME_ED448, NULL }; + return keytypes; +} +#endif + +static const OSSL_DISPATCH ecdsa_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ecdsa_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + DISPATCH_DEFN(SIGNATURE, DUPCTX, sig_dupctx), + + 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), +#ifdef OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES + DISPATCH_DEFN(SIGNATURE, QUERY_KEY_TYPES, ecdsa_query_key_types), +#endif + + DISPATCH_END, +}; + +static const OSSL_DISPATCH ed25519_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ed25519_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + DISPATCH_DEFN(SIGNATURE, DUPCTX, sig_dupctx), + + 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, 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), + +#ifdef OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT + DISPATCH_DEFN(SIGNATURE, SIGN_MESSAGE_INIT, sig_sign_init), +#endif +#ifdef OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT + DISPATCH_DEFN(SIGNATURE, VERIFY_MESSAGE_INIT, sig_verify_init), +#endif + + 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), +#ifdef OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES + DISPATCH_DEFN(SIGNATURE, QUERY_KEY_TYPES, ed25519_query_key_types), +#endif + + DISPATCH_END, +}; + +static const OSSL_DISPATCH ed448_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ed448_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + DISPATCH_DEFN(SIGNATURE, DUPCTX, sig_dupctx), + + 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, 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), + +#ifdef OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT + DISPATCH_DEFN(SIGNATURE, SIGN_MESSAGE_INIT, sig_sign_init), +#endif +#ifdef OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT + DISPATCH_DEFN(SIGNATURE, VERIFY_MESSAGE_INIT, sig_verify_init), +#endif + + 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), +#ifdef OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES + DISPATCH_DEFN(SIGNATURE, QUERY_KEY_TYPES, ed448_query_key_types), +#endif + + 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 cc4cfc4cb9eb1de170576318f8b0a8ac6e77ca24 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 9 Mar 2026 12:40:43 -0400 Subject: [PATCH 26/34] 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 d665ce35..9790c5d4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,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 a8e5c1c9..df1cd709 100644 --- a/src/provider.c +++ b/src/provider.c @@ -14,6 +14,7 @@ #include "provider.h" #include "store.h" #include "keymgmt.h" +#include "signature.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -292,6 +293,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 b0d3bce08eeec0443cc3164a9fbea0fc6f2f4733 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 10 Mar 2026 11:50:29 +0100 Subject: [PATCH 27/34] provider: Add tls-property helpers Add the supported TLS properties for the zpcprovider. 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 d272a4484c313249401808710bca0a932894b807 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 10 Mar 2026 11:51:08 +0100 Subject: [PATCH 28/34] 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 9790c5d4..866d8340 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,6 +178,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 df1cd709..ea9d7758 100644 --- a/src/provider.c +++ b/src/provider.c @@ -15,6 +15,7 @@ #include "store.h" #include "keymgmt.h" #include "signature.h" +#include "tls.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -307,6 +308,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; @@ -318,6 +330,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 0a2c8239e0609468b2f98b1f2bf70e211bec38ca Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 13 Mar 2026 17:01:36 +0100 Subject: [PATCH 29/34] 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..21b151bf --- /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(HBKZPC) = { + ASN1_EXP(HBKZPC, desc, ASN1_VISIBLESTRING, 0), + ASN1_EXP(HBKZPC, uri, ASN1_UTF8STRING, 1), +} ASN1_SEQUENCE_END(HBKZPC); + +#include "asn1_gen.c" + +int i2d_HBKZPC_bio(BIO *bp, const HBKZPC *hbkp) +{ + return ASN1_i2d_bio_of(HBKZPC, i2d_HBKZPC, bp, hbkp); +} + +HBKZPC *d2i_HBKZPC_bio(BIO *bp, HBKZPC **hbkpp) +{ + return ASN1_d2i_bio_of(HBKZPC, HBKZPC_new, d2i_HBKZPC, + bp, hbkpp); +} diff --git a/src/asn1.h b/src/asn1.h new file mode 100644 index 00000000..d2dadc1b --- /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 "HARDWARE BACKED KEY ZPC" +#define HBKZPC_DER_DESC "HBKZPC Provider URI v1.0" + +struct hbkzpc_sequence_st { + ASN1_VISIBLESTRING *desc; + ASN1_UTF8STRING *uri; +}; +typedef struct hbkzpc_sequence_st HBKZPC; +DECLARE_ASN1_FUNCTIONS(HBKZPC) + +int i2d_HBKZPC_bio(BIO *bp, const HBKZPC *hbkp); +HBKZPC *d2i_HBKZPC_bio(BIO *bp, HBKZPC **hbkpp); + +DECLARE_PEM_write_bio(HBKZPC, HBKZPC) +DECLARE_PEM_read_bio(HBKZPC, HBKZPC) +#endif /* _ASN1_H */ diff --git a/src/asn1_gen.c.in b/src/asn1_gen.c.in new file mode 100644 index 00000000..4595a483 --- /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(HBKZPC) +IMPLEMENT_PEM_write_bio(HBKZPC, HBKZPC, HBKZPC_PEM_STRING, HBKZPC) +IMPLEMENT_PEM_read_bio(HBKZPC, HBKZPC, HBKZPC_PEM_STRING, HBKZPC) From 99ee68345fddeb7915738791a65e05e4d896512f Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 13 Mar 2026 17:05:25 +0100 Subject: [PATCH 30/34] 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 866d8340..65791ccd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -167,6 +167,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 @@ -212,6 +258,7 @@ target_link_libraries( OpenSSL::Crypto $ $ + $ ) target_link_options( From 3e6c40802290d721047e52434f73e0308baec895 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sat, 14 Mar 2026 09:18:59 +0100 Subject: [PATCH 31/34] 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..48c60a7a --- /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 "hbkzpc" +#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; + HBKZPC *hbk = NULL; + BIO *bin; + + bin = BIO_new_from_core_bio(dctx->pctx->libctx, in); + if (!bin) + goto out; + + if (!d2i_HBKZPC_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); + HBKZPC_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 94895bea31239030b006fd29017e5affafdd0d9f Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sat, 14 Mar 2026 09:20:46 +0100 Subject: [PATCH 32/34] 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 65791ccd..74199190 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,6 +225,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 ea9d7758..3e3fe3ca 100644 --- a/src/provider.c +++ b/src/provider.c @@ -16,6 +16,7 @@ #include "keymgmt.h" #include "signature.h" #include "tls.h" +#include "decoder.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -297,6 +298,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 4cefb87530599538f398c3f295f14617c2a5ffa2 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 19 Aug 2025 17:55:05 +0200 Subject: [PATCH 33/34] test: Add OpenSSL configuration template To use the zpc functionality via the OpenSSL API, the zpcprovider 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. The build also configures a second template and creates a configuration drop-in file `zpcprovider.cnf`. This file can be included in existing OpenSSL configuration files. Signed-off-by: Holger Dengler --- CMakeLists.txt | 21 +++++++++++++++++++++ hbkzpcprovider.conf.in | 6 ++++++ openssl.conf.in | 25 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 hbkzpcprovider.conf.in create mode 100644 openssl.conf.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 74199190..ed96aa49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,6 +276,27 @@ target_link_options( ) endif () # BUILD_ASAN +set(OPENSSL_CONF + ${CMAKE_BINARY_DIR}/openssl.conf +) +set(OPENSSL_CONF_IN + ${CMAKE_SOURCE_DIR}/openssl.conf.in +) +set(HBKZPCPROVIDER_CONF + ${CMAKE_BINARY_DIR}/hbkzpcprovider.conf +) +set(HBKZPCPROVIDER_CONF_IN + ${CMAKE_SOURCE_DIR}/hbkzpcprovider.conf.in +) +set(ZPCPROVIDER_MODULE + zpcprovider.so +) +set(ZPCPROVIDER_MODULE_ABS + ${CMAKE_BINARY_DIR}/${ZPCPROVIDER_MODULE} +) +configure_file(${OPENSSL_CONF_IN} ${OPENSSL_CONF} @ONLY) +configure_file(${HBKZPCPROVIDER_CONF_IN} ${HBKZPCPROVIDER_CONF} @ONLY) + if(PkgConfig_FOUND) pkg_get_variable(OSSL_MODULESDIR libcrypto modulesdir) else() diff --git a/hbkzpcprovider.conf.in b/hbkzpcprovider.conf.in new file mode 100644 index 00000000..d841ebb2 --- /dev/null +++ b/hbkzpcprovider.conf.in @@ -0,0 +1,6 @@ +[provider_sect] +hbkzpc = hbkzpc_sect + +[hbkzpc_sect] +module = @ZPCPROVIDER_MODULE@ +activate = 1 diff --git a/openssl.conf.in b/openssl.conf.in new file mode 100644 index 00000000..a0d6bb17 --- /dev/null +++ b/openssl.conf.in @@ -0,0 +1,25 @@ +HOME = . + +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_ABS@ +activate = 1 From e9d06397981ecc1d9bb700b1c69aab25aadebd9d Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 07:49:18 -0500 Subject: [PATCH 34/34] dbg: Add gdb-scripts for zpcprovider The scripts set breakpoints for to all zpcprovider functions, which are called by the OpenSSL provider API (dispatch functions). Each zpcprovider component has its own gdb-script. Sourcing multiple scripts is possible. Signed-off-by: Holger Dengler --- misc/dbg/decoder.gdb | 7 +++++++ misc/dbg/kmgmt.gdb | 20 ++++++++++++++++++++ misc/dbg/provider.gdb | 8 ++++++++ misc/dbg/signature.gdb | 25 +++++++++++++++++++++++++ misc/dbg/store.gdb | 11 +++++++++++ 5 files changed, 71 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..0c24e43e --- /dev/null +++ b/misc/dbg/kmgmt.gdb @@ -0,0 +1,20 @@ +set breakpoint pending on +break kmgmt_new +break kmgmt_dup +break kmgmt_free +break kmgmt_load +break kmgmt_has +break kmgmt_match +break kmgmt_export_types +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..fcdaf3a7 --- /dev/null +++ b/misc/dbg/signature.gdb @@ -0,0 +1,25 @@ +set breakpoint pending on +break ecdsa_get_ctx_params +break ecdsa_gettable_ctx_params +break ecdsa_newctx +break ecdsa_set_ctx_params +break ecdsa_settable_ctx_params +break ed25519_newctx +break ed448_newctx +break ed_digest_sign_init +break ed_digest_verify_init +break eddsa_get_ctx_params +break eddsa_gettable_ctx_params +break eddsa_set_ctx_params +break eddsa_settable_ctx_params +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_dupctx +break sig_sign +break sig_sign_init +break sig_verify +break sig_verify_init 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