From fc22528d0b7ddaa029ec728f29ae6c845176dcfe Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Thu, 21 May 2026 10:46:11 +0200 Subject: [PATCH 01/44] 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..0b06d62d 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 and ignore return values */ + ec_key_sec2prot(ec_key1, EC_KEY_SEC_CUR); + ec_key_sec2prot(ec_key2, EC_KEY_SEC_CUR); + + /* protected key compare */ + if (ec_key1->key_set && ec_key2->key_set && + memcmp(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 aa352e663b39458220ac898d9f8b758bf8ff5318 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 21 Apr 2026 16:47:09 +0200 Subject: [PATCH 02/44] 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 44c52a6335e55da6bf82cb3cfd9b9aaebf4c21dc Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 4 May 2026 16:08:56 +0200 Subject: [PATCH 03/44] cmake: Fix broken gtest The gtest release 1.11.0 produce build problems because of outdated versions. Updating to version v1.12.1 fixes the problems. While at it, migrate from archive-download to git checkout. Signed-off-by: Holger Dengler --- CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ffb978..1b9f7ce4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,11 +146,11 @@ if (BUILD_TEST) enable_testing() -set(GTEST_URL - https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip +set(GTEST_GIT + https://github.com/google/googletest.git ) -set(GTEST_SHA256 - 353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a +set(GTEST_TAG + release-1.12.1 ) set(WYCHEPROOF_GIT @@ -355,8 +355,8 @@ ExternalProject_Add(wycheproof ) ExternalProject_Add(gtest - URL ${GTEST_URL} - URL_HASH SHA256=${GTEST_SHA256} + GIT_REPOSITORY ${GTEST_GIT} + GIT_TAG ${GTEST_TAG} PREFIX ${CMAKE_BINARY_DIR}/gtest INSTALL_COMMAND "" TEST_COMMAND "" From 6e1a7426738c1ecb0c255825030fb4bfb7f07541 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 10:07:36 +0200 Subject: [PATCH 04/44] cmake: Convert zpc target to object module The libzpc API is no longer exposed as static or shared library. The object module is only available for internal purpose. Signed-off-by: Holger Dengler --- CMakeLists.txt | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b9f7ce4..6cf5d255 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,17 +103,11 @@ set(ZPC_LIBS Threads::Threads ${CMAKE_DL_LIBS} ) -add_library(zpc ${ZPC_SOURCES}) - -set_target_properties(zpc - PROPERTIES - VERSION ${ZPC_VERSION_MAJOR}.${ZPC_VERSION_MINOR}.${ZPC_VERSION_PATCH} - SOVERSION ${ZPC_VERSION_MAJOR} - PUBLIC_HEADER "${ZPC_HEADERS}" - C_VISIBILITY_PRESET hidden - CXX_VISIBILITY_PRESET hidden - LINK_FLAGS "-Wl,--version-script=${CMAKE_SOURCE_DIR}/libzpc.map" +add_library( + zpc OBJECT + ${ZPC_SOURCES} ) + target_include_directories(zpc PRIVATE include src src/zkey) target_link_libraries(zpc ${ZPC_LIBS}) target_compile_definitions( @@ -122,21 +116,8 @@ target_compile_definitions( ZPC_VERSION_MINOR=${ZPC_VERSION_MINOR} ZPC_VERSION_PATCH=${ZPC_VERSION_PATCH} ) -configure_file(libzpc.pc.in libzpc.pc @ONLY) include(GNUInstallDirs) -install( - TARGETS zpc - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/zpc/ -) - -install( - FILES ${CMAKE_BINARY_DIR}/libzpc.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig -) - ########################################################### # Test @@ -375,7 +356,7 @@ set(ZPC_TEST_LIBS ${GTEST_LIB_DIR}/libgtest.a Threads::Threads json-c::json-c - zpc + $ ) set(ZPC_TEST_SOURCES From e08b14e4f5f6d4327560ca6ac145d72cdec8d358 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 11 May 2026 09:04:30 +0200 Subject: [PATCH 05/44] cmake: Introduce build option BUILD_INTERNAL_TEST As the libzpc API is no longer externally available, also the extensive testing (gtest/wycheproof) has to be made internal. Introduce a new build option BUILD_INTERNAL_TEST. Enabling this new option will build the extensive tests. Signed-off-by: Holger Dengler --- CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6cf5d255..46706b1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,8 +122,9 @@ include(GNUInstallDirs) # Test option(BUILD_TEST OFF) +option(BUILD_INTERNAL_TEST OFF) -if (BUILD_TEST) +if (BUILD_INTERNAL_TEST) enable_testing() @@ -404,7 +405,7 @@ target_include_directories(runtest PRIVATE include src ${GTEST_INCLUDE_DIR}) include(GoogleTest) gtest_discover_tests(runtest) -endif () +endif () # BUILD_INTERNAL_TEST ########################################################### # doc From 7ef2cba0f2d393a4281678cb07f6d73a624615f7 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 07:35:20 +0200 Subject: [PATCH 06/44] cmake: Harmonize indent Adjust indention, no functional change. Signed-off-by: Holger Dengler --- CMakeLists.txt | 70 +++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46706b1b..7b902bc7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,7 +100,7 @@ add_definitions( ) set(ZPC_LIBS - Threads::Threads ${CMAKE_DL_LIBS} + Threads::Threads ${CMAKE_DL_LIBS} ) add_library( @@ -207,19 +207,19 @@ set(NIST_AES_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_aes_ecb.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_ecb.pl - ${NIST_AES_SOURCE_DIR}/ECBMMT* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes + OUTPUT nist_aes_ecb.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_ecb.pl + ${NIST_AES_SOURCE_DIR}/ECBMMT* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes ) add_custom_target(nist_aes_ecb_json ALL DEPENDS nist_aes_ecb.json) add_custom_command( - OUTPUT nist_aes_cbc.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_cbc.pl - ${NIST_AES_SOURCE_DIR}/CBCMMT* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes + OUTPUT nist_aes_cbc.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_cbc.pl + ${NIST_AES_SOURCE_DIR}/CBCMMT* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes ) add_custom_target(nist_aes_cbc_json ALL DEPENDS nist_aes_cbc.json) @@ -237,11 +237,11 @@ set(NIST_AES_XTS_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_aes_xts.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_xts.pl - ${NIST_AES_XTS_SOURCE_DIR}/'format tweak value input - 128 hex str'/* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes_xts + OUTPUT nist_aes_xts.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_xts.pl + ${NIST_AES_XTS_SOURCE_DIR}/'format tweak value input - 128 hex str'/* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes_xts ) add_custom_target(nist_aes_xts_json ALL DEPENDS nist_aes_xts.json) @@ -259,11 +259,11 @@ set(NIST_AES_GCM_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_aes_gcm.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_gcm.pl - ${NIST_AES_GCM_SOURCE_DIR}/* - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_aes_gcm + OUTPUT nist_aes_gcm.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_aes_gcm.pl + ${NIST_AES_GCM_SOURCE_DIR}/* + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_aes_gcm ) add_custom_target(nist_aes_gcm_json ALL DEPENDS nist_aes_gcm.json) @@ -309,20 +309,20 @@ set(NIST_ECDSA_SOURCE_DIR ${SOURCE_DIR} ) add_custom_command( - OUTPUT nist_ecdsa.json - COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_ecdsa.pl - ${NIST_ECDSA_SOURCE_DIR}/SigGenComponent.txt - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - DEPENDS nist_ecdsa + OUTPUT nist_ecdsa.json + COMMAND ${CMAKE_SOURCE_DIR}/misc/nist2json_ecdsa.pl + ${NIST_ECDSA_SOURCE_DIR}/SigGenComponent.txt + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + DEPENDS nist_ecdsa ) add_custom_target(nist_ecdsa_json ALL DEPENDS nist_ecdsa.json) add_custom_command( - OUTPUT nist_eddsa.json - COMMAND ${CMAKE_COMMAND} -E copy - ${CMAKE_SOURCE_DIR}/misc/nist_eddsa.json - ${CMAKE_BINARY_DIR} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + OUTPUT nist_eddsa.json + COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_SOURCE_DIR}/misc/nist_eddsa.json + ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) add_custom_target(nist_eddsa_json ALL DEPENDS nist_eddsa.json) @@ -339,10 +339,10 @@ ExternalProject_Add(wycheproof ExternalProject_Add(gtest GIT_REPOSITORY ${GTEST_GIT} GIT_TAG ${GTEST_TAG} - PREFIX ${CMAKE_BINARY_DIR}/gtest - INSTALL_COMMAND "" - TEST_COMMAND "" - CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" + PREFIX ${CMAKE_BINARY_DIR}/gtest + INSTALL_COMMAND "" + TEST_COMMAND "" + CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" ) ExternalProject_Get_Property(gtest SOURCE_DIR BINARY_DIR) set(GTEST_INCLUDE_DIR From 1021ca980843f6898864f6390bb08285e789c172 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sun, 17 May 2026 15:04:03 +0200 Subject: [PATCH 07/44] 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 7b902bc7..16bce897 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -435,3 +435,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 365e7f3e484fde27ab3f714674f00566287cd5d2 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 20:11:16 +0200 Subject: [PATCH 08/44] 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 16bce897..a15a6b64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,11 @@ find_package(json-c REQUIRED ) +find_package(OpenSSL + 3.0.7 + REQUIRED +) + add_definitions( -D_GNU_SOURCE ) From b7376cdb151f6ac45ce9c0a2429d58f148a26e15 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 27 May 2026 21:30:03 +0200 Subject: [PATCH 09/44] 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 a15a6b64..b4df51ff 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 40180cd245c84a716f3dde2b3bbad331e603acb3 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 16:42:12 +0200 Subject: [PATCH 10/44] 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 | 125 ++++++++++++ src/ossl.h | 22 +++ src/provider.c | 360 +++++++++++++++++++++++++++++++++++ src/provider.h | 66 +++++++ 5 files changed, 749 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..563094c3 --- /dev/null +++ b/man/hbkzpcprovider.conf.5.md @@ -0,0 +1,125 @@ +% HBKZPCPROVIDER.CNF(5) LIBZPC v2 +% +% 2026 + +# NAME + +hbkzpcprovider.conf - Configuration syntax for the `hbkzpc provider` + +# DESCRIPTION + +This page documents the syntax of 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 a 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 should be referenced +in the global providers sections (*provider_sect*) of the OpenSSL configuration +file. The `hbkzpc provider` supports no provider-specific parameters, but it +requires at least the generic provider section parameter *module* and +*activate*. 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 will be + activated. Conversely, setting this value to `0` or not specifying the + parameter at all will prevent the provider from being activated (default: + `0`). + +The `identity` parameter might 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 + +This drop-in example specifies the 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 (e.g. +`/etc/pki/tls/openssl.d/` on Fedora). + + .include /path/to/drop-in-directory + +This example covers a complete minimal OpenSSL configuration. It is used +e.g. for running the test scripts of the project. + + 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 list 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..2ab36556 --- /dev/null +++ b/src/provider.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _PROVIDER_H +#define _PROVIDER_H + +#include +#include +#include + +#define PROV_NAME "hbkzpc" +#define PROV_VERSION "2.0.0" +#define PROV_PROP "provider="PROV_NAME +#define PROV_PROP_FWD "provider!="PROV_NAME + +#define PROV_NAME_EC "EC" +#define PROV_NAMES_EC "EC:id-ecPublicKey:1.2.840.10045.2.1" +#define PROV_DESC_EC "hbkzpc EC implementation" + +#define PROV_NAME_ECDSA "ECDSA" +#define PROV_NAMES_ECDSA PROV_NAME_ECDSA +#define PROV_DESC_ECDSA "hbkzpc ECDSA Implementation" + +#define PROV_NAME_ED25519 "ED25519" +#define PROV_NAMES_ED25519 "ED25519:1.3.101.112" +#define PROV_DESC_ED25519 "hbkzpc ED25519 Implementation" + +#define PROV_NAME_ED448 "ED448" +#define PROV_NAMES_ED448 "ED448:1.3.101.113" +#define PROV_DESC_ED448 "hbkzpc ED448 Implementation" + +#define __unused __attribute__((unused)) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +enum provider_state { + PROVIDER_UNINITIALIZED = 0, + PROVIDER_INITIALIZED, +}; + +struct provider_ctx { + const OSSL_CORE_HANDLE *handle; + OSSL_LIB_CTX *libctx; + + enum provider_state state; + + OSSL_FUNC_core_new_error_fn *core_new_error; + OSSL_FUNC_core_set_error_debug_fn *core_set_error_debug; + OSSL_FUNC_core_vset_error_fn *core_vset_error; +}; + +void prov_err_raise(struct provider_ctx *pctx, const char *file, int line, + const char *func, int reason, const char *fmt, ...); +#define PROV_ERR_raise(pctx, reason) \ + prov_err_raise(pctx, OPENSSL_FILE, OPENSSL_LINE, OPENSSL_FUNC, reason, NULL) + +#endif /* _PROVIDER_H */ From 39d61f900d6e53fe67289b9c7f474f1219ae877e Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 1 Oct 2025 20:11:48 +0200 Subject: [PATCH 11/44] 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 | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ zpcprovider.map | 6 ++++ 2 files changed, 79 insertions(+) create mode 100644 zpcprovider.map diff --git a/CMakeLists.txt b/CMakeLists.txt index b4df51ff..f7eecae7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,10 @@ find_package(OpenSSL REQUIRED ) +find_package(PkgConfig + QUIET +) + add_definitions( -D_GNU_SOURCE ) @@ -137,6 +141,75 @@ target_compile_definitions( ) include(GNUInstallDirs) +########################################################### +# zpcprovider + +set(ZPCPROVIDER_SOURCES + src/provider.c +) + +add_library( + zpcprovider MODULE + ${ZPCPROVIDER_SOURCES} +) + +set_target_properties( + zpcprovider + PROPERTIES + PREFIX "" +) + +target_include_directories( + zpcprovider + PRIVATE + src + include + ${OPENSSL_INCLUDE_DIR} +) + +target_link_libraries( + zpcprovider + PRIVATE + OpenSSL::Crypto +) + +target_link_options( + zpcprovider + PRIVATE + -Wl,--version-script=${CMAKE_SOURCE_DIR}/zpcprovider.map +) + +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 65454d8e970674a3c474e81796b533ed9d63f7a0 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 14:07:00 +0100 Subject: [PATCH 12/44] 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 0591e1460f8110f83d759269033df956b2e36945 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 14:36:47 +0100 Subject: [PATCH 13/44] 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 f7eecae7..21f32980 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -146,6 +146,7 @@ include(GNUInstallDirs) set(ZPCPROVIDER_SOURCES src/provider.c + src/object.c ) add_library( From 66915cf524e812c0c0735bf6aaccbd0caf86fde8 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:53:09 +0100 Subject: [PATCH 14/44] 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 0fa93f3f59ccdccb30e544224d2193013bf487fa Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:54:30 +0100 Subject: [PATCH 15/44] 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 21f32980..e9fec92c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -141,6 +141,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 @@ -172,6 +196,7 @@ target_link_libraries( zpcprovider PRIVATE OpenSSL::Crypto + $ ) target_link_options( From 99f4780f1a97202473aa67bb446c758e7d8e2279 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 03:21:27 -0500 Subject: [PATCH 16/44] 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 034dfb7f06d81b7fc25eb6d73a4909ae30b3eeb4 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 03:22:34 -0500 Subject: [PATCH 17/44] 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 e9fec92c..1620dcef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ target_include_directories( set(ZPCPROVIDER_SOURCES src/provider.c src/object.c + src/map.c ) add_library( From bfb8e0c83ad4226032e5540e58428f0128f7a68f Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 20 Apr 2026 11:23:25 +0200 Subject: [PATCH 18/44] 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 3c9e912c2bea717d8c4fc476a8102064a6baf823 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 21 Jan 2026 16:59:57 +0100 Subject: [PATCH 19/44] 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 1620dcef..4fd71e41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -172,6 +172,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 e4fa7160ef08252024d853bd9c67ad0667d36e1a Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 18 Feb 2026 04:37:32 -0500 Subject: [PATCH 20/44] 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 a0922c10c9e4e1bb197a5326ee04ee7df260c24d Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 5 May 2026 10:08:31 +0200 Subject: [PATCH 21/44] cmake: Integrate asymmetric key management Signed-off-by: Holger Dengler --- CMakeLists.txt | 2 ++ src/provider.c | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fd71e41..db74b277 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,7 @@ set(ZPCPROVIDER_SOURCES src/object.c src/map.c src/store.c + src/keymgmt.c ) add_library( @@ -199,6 +200,7 @@ target_link_libraries( PRIVATE OpenSSL::Crypto $ + $ ) target_link_options( diff --git a/src/provider.c b/src/provider.c index d720a265..540455c7 100644 --- a/src/provider.c +++ b/src/provider.c @@ -12,6 +12,7 @@ #include "ossl.h" #include "provider.h" #include "store.h" +#include "keymgmt.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -285,6 +286,9 @@ static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, case OSSL_OP_STORE: ops = store_ops; break; + case OSSL_OP_KEYMGMT: + ops = keymgmt_ops; + break; default: ops = NULL; goto out; From 3dc9b5f6e5157b2d2ff8dc229e643dfb28c224cd Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 27 Apr 2026 16:53:47 +0200 Subject: [PATCH 22/44] 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 a4af845d9cb823de3230320d6aba3fa9fbb44933 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 27 Apr 2026 16:54:36 +0200 Subject: [PATCH 23/44] 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 db74b277..4b82f544 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,7 @@ set(ZPCPROVIDER_SOURCES src/map.c src/store.c src/keymgmt.c + src/algid.c ) add_library( From 23ca671425cabac1dbb1fed81480faf543e68008 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 9 Mar 2026 12:37:49 -0400 Subject: [PATCH 24/44] provider: Add signature algorithms Add signature algorithms for sign/verify with ECDSA and EDDSA keys. Signed-off-by: Holger Dengler --- src/signature.c | 886 ++++++++++++++++++++++++++++++++++++++++++++++++ src/signature.h | 10 + 2 files changed, 896 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..ddad2547 --- /dev/null +++ b/src/signature.c @@ -0,0 +1,886 @@ +// 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_NUMBER < 0x30100000L +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; +} + +static const OSSL_DISPATCH ecdsa_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ecdsa_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, VERIFY_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_INIT, sig_digest_sign_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_UPDATE, sig_digest_update), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_FINAL, sig_digest_sign_final), + + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_INIT, sig_digest_verify_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_UPDATE, sig_digest_update), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_FINAL, sig_digest_verify_final), + + DISPATCH_DEFN(SIGNATURE, GETTABLE_CTX_PARAMS, ecdsa_gettable_ctx_params), + DISPATCH_DEFN(SIGNATURE, GET_CTX_PARAMS, ecdsa_get_ctx_params), + DISPATCH_DEFN(SIGNATURE, SETTABLE_CTX_PARAMS, ecdsa_settable_ctx_params), + DISPATCH_DEFN(SIGNATURE, SET_CTX_PARAMS, ecdsa_set_ctx_params), + + DISPATCH_END, +}; + +static const OSSL_DISPATCH ed25519_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ed25519_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_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), + + 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), + + 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 5f2d2f21e0763c1f5f36da86e71d78edb0a9b793 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 9 Mar 2026 12:40:43 -0400 Subject: [PATCH 25/44] 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 4b82f544..615d504c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -175,6 +175,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 540455c7..e9256850 100644 --- a/src/provider.c +++ b/src/provider.c @@ -13,6 +13,7 @@ #include "provider.h" #include "store.h" #include "keymgmt.h" +#include "signature.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -289,6 +290,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 c11abb7ef0c7f5e89c9fa4129b4c22c35e9ac623 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 10 Mar 2026 11:50:29 +0100 Subject: [PATCH 26/44] 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 645f1ce0216d2025e431bbf01651f87b6f4ce5a9 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 10 Mar 2026 11:51:08 +0100 Subject: [PATCH 27/44] 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 615d504c..408f86d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -176,6 +176,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 e9256850..090f8aee 100644 --- a/src/provider.c +++ b/src/provider.c @@ -14,6 +14,7 @@ #include "store.h" #include "keymgmt.h" #include "signature.h" +#include "tls.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -304,6 +305,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; @@ -315,6 +327,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 22cbdffe16e813d439b1120e45a557b158fa7fc6 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 13 Mar 2026 17:01:36 +0100 Subject: [PATCH 28/44] 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 6f3c64d04f0f32f81bf88238259cb3f477592d6a Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 13 Mar 2026 17:05:25 +0100 Subject: [PATCH 29/44] 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 408f86d3..674684d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,6 +165,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 @@ -204,6 +250,7 @@ target_link_libraries( OpenSSL::Crypto $ $ + $ ) target_link_options( From 09c53c4b7d261d1fde9374fa6545094612e1ed25 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sat, 14 Mar 2026 09:18:59 +0100 Subject: [PATCH 30/44] 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 bc3bc4c0f4bcbaa1367b89555ff8dc9e988a8854 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sat, 14 Mar 2026 09:20:46 +0100 Subject: [PATCH 31/44] 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 674684d1..27f04647 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -223,6 +223,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 090f8aee..ca885308 100644 --- a/src/provider.c +++ b/src/provider.c @@ -15,6 +15,7 @@ #include "keymgmt.h" #include "signature.h" #include "tls.h" +#include "decoder.h" #define C(str) (void *)(str) static const OSSL_ITEM reason_strings[] = { @@ -294,6 +295,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 aeebd1b2522b7a7a0c1c78fc60453b20ac8c83ad Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Tue, 19 Aug 2025 17:55:05 +0200 Subject: [PATCH 32/44] 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 27f04647..552e06d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,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 996860ef8c2b133e50a7cf1a4345673636972343 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 25 Feb 2026 07:49:18 -0500 Subject: [PATCH 33/44] 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 From d760921df26ffb5ea7dd57af8a7c8ea0f6101a85 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 20 Apr 2026 08:33:37 +0200 Subject: [PATCH 34/44] uri: Add URI compose function Signed-off-by: Holger Dengler --- src/uri.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/uri.h | 5 +++ 2 files changed, 121 insertions(+) diff --git a/src/uri.c b/src/uri.c index 9f30692b..88626467 100644 --- a/src/uri.c +++ b/src/uri.c @@ -22,6 +22,69 @@ #define URI_Q_MKVP "mkvp" SEP_KEYVALUE #define URI_Q_APQNS "apqns" SEP_KEYVALUE +#ifndef ROUND +#define ROUND(a, b) ((((a) + (b - 1)) / (b)) * b) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*a)) +#endif + +struct astr { + char *b; + size_t bsz; +}; + +struct astr *astr_new(void) +{ + return OPENSSL_zalloc(sizeof(struct astr)); +} + +static void astr_free(struct astr *a) +{ + if (!a) + return; + OPENSSL_free(a->b); + OPENSSL_free(a); +} + +#define ASTR_CHUNK_SZ 32 +static int astr_append(struct astr *a, const char *str) +{ + size_t sz = 1; + + if (!a) + return 1; + + if (!str || !strlen(str)) + return 0; + + /* required size */ + sz += strlen(str); + sz += a->b ? strlen(a->b) : 0; + sz = ROUND(sz, ASTR_CHUNK_SZ); + + if (sz > a->bsz) { + char *p; + + if (!(p = OPENSSL_realloc(a->b, sz))) + return 1; + if (!a->bsz) + p[0] = '\0'; + a->b = p; + } + a->bsz = sz; + + strncpy(a->b + strlen(a->b), str, a->bsz - strlen(a->b)); + return 0; +} +#undef ASTR_CHUNK_SZ + +static const char *astr_cstring(struct astr *a) +{ + return a ? a->b : NULL; +} + static void decode_pct(char *s) { char *rp, *wp, *endptr; @@ -218,3 +281,56 @@ struct parsed_uri *parsed_uri_new(const char *uri) parsed_uri_free(puri); return NULL; } + +char *uri_compose_new(const char *origin_type, const char *origin_alg, + const char *origin_blob, const char *origin_pubkey, + const char *comment, + const char *mkvp, const char *apqns) +{ + struct attr pattrs[] = { + { .key = URI_P_ORIGIN_TYPE, .value = origin_type }, + { .key = URI_P_ORIGIN_ALG, .value = origin_alg }, + { .key = URI_P_ORIGIN_BLOB, .value = origin_blob }, + { .key = URI_P_ORIGIN_PUBKEY, .value = origin_pubkey }, + { .key = URI_P_COMMENT, .value = comment }, + }; + struct attr qattrs[] = { + { .key = URI_Q_MKVP, .value = mkvp }, + { .key = URI_Q_APQNS, .value = apqns }, + }; + const char *sep = URI_PROTOCOL; + struct astr *astr = NULL; + char *rc = NULL; + + astr = astr_new(); + for (size_t i = 0; i < ARRAY_SIZE(pattrs); i++) { + struct attr *a = &pattrs[i]; + if (!a->value) + continue; + + if (astr_append(astr, sep) || + astr_append(astr, a->key) || + astr_append(astr, a->value)) + goto out; + sep = SEP_PATHATTRS; + } + + sep = SEP_PATHQUERY; + + for (size_t i = 0; i < ARRAY_SIZE(qattrs); i++) { + struct attr *a = &qattrs[i]; + if (!a->value) + continue; + + if (astr_append(astr, sep) || + astr_append(astr, a->key) || + astr_append(astr, a->value)) + goto out; + sep = SEP_QUERYATTRS; + } + + rc = OPENSSL_strdup(astr_cstring(astr)); +out: + astr_free(astr); + return rc; +} diff --git a/src/uri.h b/src/uri.h index 79cb7c14..eb2a18c2 100644 --- a/src/uri.h +++ b/src/uri.h @@ -33,4 +33,9 @@ struct parsed_uri { struct parsed_uri *parsed_uri_new(const char *uri); void parsed_uri_free(struct parsed_uri *puri); +char *uri_compose_new(const char *origin_type, const char *origin_alg, + const char *origin_blob, const char *origin_spki, + const char *comment, + const char *mkvp, const char *apqns); + #endif /* _URI_H */ From 0b65b62cda8b34d529b59e4ca0a82f9083bc8ad0 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 20 Apr 2026 08:35:49 +0200 Subject: [PATCH 35/44] tools: Add zpckey management tool The tool `zpckey` is a key management tool for the zpcprovider. It supports the composition of key-origins (compose) and prints information about existing zpcprovider keys (show). The tool supports key encoding as hbkzpc-URI, DER or PEM. Currently, only keys of origin type `uv` are supported. Signed-off-by: Holger Dengler --- man/zpckey.1.md | 115 ++++++++++ src/zpckey.c | 560 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 675 insertions(+) create mode 100644 man/zpckey.1.md create mode 100644 src/zpckey.c diff --git a/man/zpckey.1.md b/man/zpckey.1.md new file mode 100644 index 00000000..97b3de55 --- /dev/null +++ b/man/zpckey.1.md @@ -0,0 +1,115 @@ +% ZPCKEY(1) LIBZPC v2 +% +% 2026 + +# NAME + +zpckey - A key management tooling for protected key origins, used by the OpenSSL +provider for protected keys (`hbkzpc provider`). + +# SYNOPSIS + +zpckey [-h|--help] [-V|--version] + +zpckey compose \ [\] \ + +zpckey show \ [\] + +# DESCRIPTION + +The zpckey command provides key management functions for protected key origins, +used by the `hbkzpc provider` (hbkzpcprovider(7)). IBM Z and IBM LinuxONE offer +different types of cryptographic hardware with different features, including the +CP Assist for Cryptographic Functions (CPACF) and the IBM Crypto Express (CEX) +features. + +The CPACF provides functions to perform cryptographic operations with a kind of +hardware-backed keys, the so called protected keys. + +The CEX cards provide secure key generation and storage (secure keys), as well +as cryptographic operations with these keys. + +# OPTIONS + +-V, \--version +: Show version + +-h, \--help +: Show short help + +# ZPCKEY COMPOSE + +The *compose* command supports the composition of hardware-backed key origins, +which can be used for the `hbkzpc provider`. + +## Required Arguments: + +-t, \--origin-type \ +: Protected key origin type + +-a, \--origin-alg \ +: Protected key origin algorithm + +## Optional Arguments: + +-p, \--pubkey \ +: Public key file + +-o, \--out \ +: Output file + +\--outform \ +: Output file format URI, DER or PEM (default: PEM) + +-c, \--comment \ +: Comment (metadata) + +-h, \--help +: Show short help + +## Origin Arguments: + +For origin type *uv*: +: \--uv-secret-id \ + : UV secret ID + +: \--uv-secret-name \ + : UV secret name + +## Protected key origin types (OTYPE): + +- uv: Ultravisor retrievable secrets + +## Protected key origin algorithms (OALG): + +- prime256v1 (alt.: 1.2.840.10045.3.1.7) +- secp384r1 (alt.: 1.3.132.0.34) +- secp521r1 (alt.: 1.3.132.0.35) +- ED25519 (alt.: 1.3.101.112) +- ED448 (alt.: 1.3.101.113) +- AES-128 +- AES-192 +- AES-256 +- AES-128-XTS +- AES-256-XTS + +# ZPCKEY SHOW + +The *show* command prints information about the key file to stdout. + +## Required Arguments: + +-i, \--in \ +: Input file + +## Optional Arguments: + +\--inform \ +: Input file format PEM or DER (default: PEM) + +-h, \--help +: Show short help + +# SEE ALSO + +hbkzpcprovider.conf(5), hbkzpcprovider(7) diff --git a/src/zpckey.c b/src/zpckey.c new file mode 100644 index 00000000..4e05031b --- /dev/null +++ b/src/zpckey.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +/* missing in obj_mac.h */ +#ifndef SN_aes_128 +#define SN_aes_128 "AES-128" +#endif +#ifndef SN_aes_192 +#define SN_aes_192 "AES-192" +#endif +#ifndef SN_aes_256 +#define SN_aes_256 "AES-256" +#endif + +#include "ossl.h" +#include "misc.h" +#include "asn1.h" +#include "uri.h" +#include "zkey/pkey.h" + +#ifndef XOR_LOG +#define XOR_LOG(a, b) (!(a) != !(b)) +#endif + +static const char *comm = NULL; +static unsigned char id_raw[32] = { 0 }; +static char id_hex[64 + 1] = { 0 }; + +enum outform { + UNKNOWN = 0, + URI, + DER, + PEM, +}; + +struct outform_entry { + enum outform id; + const char *str; +}; + +const struct outform_entry outform_map[] = { + { URI, "URI" }, + { DER, "DER" }, + { PEM, "PEM" }, + { 0, NULL }, +}; + +struct origin_type_entry { + const char *type; + const char *type_long; +}; + +static struct origin_type_entry origin_type_map[] = { + { "uv", "Ultravisor retrievable secrets" }, + { NULL, NULL }, +}; + +struct origin_alg_entry { + const char *alg; + const char *oid; + bool asym; +}; + +static struct origin_alg_entry origin_alg_list[] = { + { SN_X9_62_prime256v1, "1.2.840.10045.3.1.7", true }, + { SN_secp384r1, "1.3.132.0.34", true }, + { SN_secp521r1, "1.3.132.0.35", true }, + { SN_ED25519, "1.3.101.112", true }, + { SN_ED448, "1.3.101.113", true }, + { SN_aes_128, NULL, false }, + { SN_aes_192, NULL, false }, + { SN_aes_256, NULL, false }, + { SN_aes_128_xts, NULL, false }, + { SN_aes_256_xts, NULL, false }, + { NULL, NULL, false }, +}; + +static struct option options_compose[] = { + { "origin-type", required_argument, NULL, 't'}, + { "origin-alg", required_argument, NULL, 'a'}, + { "pubkey", required_argument, NULL, 'p'}, + { "comment", required_argument, NULL, 'c'}, + { "uv-secret-id", required_argument, NULL, 'S'}, + { "uv-secret-name", required_argument, NULL, 'N'}, + { "out", required_argument, NULL, 'o'}, + { "outform", required_argument, NULL, 'O'}, + { "help", no_argument, NULL, 'h'}, + { NULL, 0, NULL, 0}, +}; +static const char *o_compose = "t:a:p:c:o:h"; + +static struct option options_show[] = { + { "in", required_argument, NULL, 'i'}, + { "inform", required_argument, NULL, 'I'}, + { "help", no_argument, NULL, 'h'}, + { NULL, 0, NULL, 0}, +}; +static const char *o_show = "i:h"; + +struct opts_compose { + const char *otype; + const char *oalg; + const char *comment; + union { + struct { + const char *id_hex; + } uv; + }; + const char *pubkey; + const char *out; + enum outform outform; + bool asym; +}; + +struct opts_show { + const char *in; + bool in_der; +}; + +static int usage_compose(const char *comm, const char *msg, const char *arg) +{ + struct origin_type_entry *ot; + struct origin_alg_entry *oa; + + if (msg) + fprintf(stderr, "%s%s\n", msg, arg ?: ""); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s compose [] \n", comm); + fprintf(stderr, "\nRequired Arguments:\n"); + fprintf(stderr, " -t, --origin-type Protected Key Origin Type\n"); + fprintf(stderr, " -a, --origin-alg Protected Key Origin Algorithm\n"); + fprintf(stderr, "\nOptional Arguments:\n"); + fprintf(stderr, " -p, --pubkey Public Key file\n"); + fprintf(stderr, " -o, --out Output file\n"); + fprintf(stderr, " --outform Output file format URI, DER or PEM (default: PEM)\n"); + fprintf(stderr, " -c, --comment Comment (metadata)\n"); + fprintf(stderr, " -h, --help Show short help\n"); + fprintf(stderr, "\nProtected Key Origin Types (OTYPE):\n"); + for (ot = origin_type_map; ot->type; ot++) + fprintf(stderr, " %-30s%s\n", ot->type, ot->type_long); + fprintf(stderr, "\nProtected Key Origin Algorithms (OALG):\n"); + for (oa = origin_alg_list; oa->alg; oa++) + oa->oid ? fprintf(stderr, " %s (alt.: %s)\n", oa->alg, oa->oid) + : fprintf(stderr, " %s\n", oa->alg); + fprintf(stderr, "\nOrigin Arguments:\n"); + fprintf(stderr, " For origin type uv:\n"); + fprintf(stderr, " --uv-secret-id UV secret ID\n"); + fprintf(stderr, " --uv-secret-name UV secret name\n"); + + return 1; +} + +static int usage_show(const char *comm, const char *msg, const char *arg) +{ + if (msg) + fprintf(stderr, "%s%s\n", msg, arg ?: ""); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s show []\n", comm); + fprintf(stderr, "\nRequired Arguments:\n"); + fprintf(stderr, " -i, --in Input file\n"); + fprintf(stderr, "\nOptional Arguments:\n"); + fprintf(stderr, " --inform Input file format PEM or DER (default: PEM)\n"); + fprintf(stderr, " -h, --help Show short help\n"); + + return 1; +} + +static inline const char *skip_0x(const char *optarg) +{ + return (OPENSSL_strncasecmp(optarg, "0x", 2) == 0) ? + &optarg[2] : + optarg; +} + +static int check_uv_id(const char *uv_id) +{ + size_t len; + + if (!OPENSSL_hexstr2buf_ex(NULL, 0, &len, uv_id, '\0')) { + fprintf(stderr, "uv-secret-id: wrong format (hex)\n"); + return 1; + } + + if (len != 32) { + fprintf(stderr, "uv-secret-id: incorrect length (%lu != 32)\n", len); + return 1; + } + + return 0; +} + +static char *get_raw_pubkey_hex(const char *file) +{ + unsigned char *rpub, *p = NULL; + size_t publen = 0, hexlen = 0; + EVP_PKEY *pkey = NULL; + char *hex, *rc = NULL; + BIO *bi = NULL; + + bi = BIO_new_file(file, "r"); + if (!bi) { + fprintf(stderr, "Unable to read %s\n", file); + ERR_print_errors_fp(stderr); + goto out; + } + + if (!PEM_read_bio_PUBKEY(bi, &pkey, NULL, NULL)) { + fprintf(stderr, "PEM_read_bio_PUBKEY() %s\n", file); + ERR_print_errors_fp(stderr); + goto out; + } + + if (EVP_PKEY_get_raw_public_key(pkey, NULL, &publen) == OSSL_RV_OK) { + if (((p = OPENSSL_zalloc(publen)) == NULL) || + (EVP_PKEY_get_raw_public_key(pkey, p, &publen) != OSSL_RV_OK)) { + fprintf(stderr, "unable to get raw public key: %s\n", file); + ERR_print_errors_fp(stderr); + goto out; + } + rpub = p; + } else { + if ((publen = EVP_PKEY_get1_encoded_public_key(pkey, &p)) == 0) { + fprintf(stderr, "unable to get encoded public key: %s\n", file); + ERR_print_errors_fp(stderr); + goto out; + } + + /* skip format byte */ + rpub = p + 1; + publen--; + } + + // hex-hex + if ((OPENSSL_buf2hexstr_ex(NULL, 0, &hexlen, rpub, publen, '\0') != OSSL_RV_OK) || + ((hex = OPENSSL_malloc(hexlen)) == NULL) || + (OPENSSL_buf2hexstr_ex(hex, hexlen, &hexlen, rpub, publen, '\0') != OSSL_RV_OK)) { + fprintf(stderr, "Unable to hex-encode public key: %s\n", file); + goto out; + } + rc = (char *)hex; +out: + OPENSSL_free(p); + EVP_PKEY_free(pkey); + BIO_free(bi); + return rc; +} + +static int compose(struct opts_compose *o) +{ + HBKZPC *hbk = NULL; + char *uri = NULL, *pub_hex = NULL; + const char *blob_hex = NULL; + BIO *bo = NULL; + int rc = 1; + + if (!o) + return 1; + + if (o->pubkey && !(pub_hex = get_raw_pubkey_hex(o->pubkey))) { + fprintf(stderr, "Unable to fetch pubkey\n"); + goto out; + } + + if (strcmp(o->otype, "uv") == 0) + blob_hex = o->uv.id_hex; + + // URI + uri = uri_compose_new(o->otype, o->oalg, + blob_hex, pub_hex, + o->comment, + NULL, NULL); + + // DER + hbk = HBKZPC_new(); + if (!hbk) { + fprintf(stderr, "Unable to create ASN1 structure\n"); + goto out; + } + + ASN1_STRING_set(hbk->desc, HBKZPC_DER_DESC, -1); + ASN1_STRING_set(hbk->uri, uri, -1); + + /* output BIO */ + bo = (strcmp(o->out, "-") == 0) + ? BIO_new_fp(stdout, BIO_NOCLOSE) + : BIO_new_file(o->out, "w"); + + if (!bo) { + fprintf(stderr, "Unable to open file: %s\n", o->out); + goto out; + } + + switch (o->outform) { + case URI: + if (BIO_printf(bo, "%s\n", uri) != (int)strlen(uri) + 1) { + fprintf(stderr, "Unable to write URI to %s\n", o->out); + goto out; + } + break; + case DER: + if (!i2d_HBKZPC_bio(bo, hbk)) { + fprintf(stderr, "Unable to write DER file %s\n", o->out); + goto out; + } + break; + case PEM: + if (!PEM_write_bio_HBKZPC(bo, hbk)) { + fprintf(stderr, "Unable to write DER file %s\n", o->out); + goto out; + } + break; + default: + break; + } + + rc = 0; +out: + OPENSSL_free(uri); + OPENSSL_free(pub_hex); + HBKZPC_free(hbk); + BIO_free(bo); + + return rc; +} + +int main_compose(int argc, char * const argv[]) +{ + struct opts_compose opts = { + .out = "-", + .outform = PEM, + }; + int c; + + while(1) { + const struct origin_type_entry *ot; + const struct origin_alg_entry *oa; + const struct outform_entry *of; + + c = getopt_long(argc, argv, o_compose, options_compose, NULL); + if (c < 0) + break; + + switch (c) { + case 't': + for(ot = origin_type_map; ot->type; ot++) { + if (strcasecmp(optarg, ot->type) == 0) + opts.otype = ot->type; + } + if (!opts.otype) + return usage_compose(comm, "Origin type not supported: ", optarg); + break; + case 'a': + for(oa = origin_alg_list; oa->alg; oa++) { + if ((strcasecmp(optarg, oa->alg) == 0) || + (oa->oid && + strcasecmp(optarg, oa->oid) == 0)) { + opts.oalg = oa->alg; + opts.asym = oa->asym; + } + } + if (!opts.oalg) + return usage_compose(comm, "Origin algorithm not supported: ", optarg); + break; + case 'p': + opts.pubkey = optarg; + break; + case 'o': + opts.out = optarg; + break; + case 'O': + for(of = outform_map; of->str; of++) { + if (strcasecmp(of->str, optarg) == 0) + opts.outform = of->id; + } + break; + case 'S': + if (opts.uv.id_hex) + return usage_compose(comm, "uv: Only uv-secret-name or uv-secret-id is supported", NULL); + + opts.uv.id_hex = skip_0x(optarg); + if (check_uv_id(opts.uv.id_hex)) + return usage_compose(comm, NULL, NULL); + break; + case 'N': + if (opts.uv.id_hex) + return usage_compose(comm, "uv: Only uv-secret-name or uv-secret-id is supported", NULL); + if (!SHA256((const unsigned char *)optarg, strlen(optarg), id_raw) || + (OPENSSL_buf2hexstr_ex(id_hex, 65, NULL, id_raw, sizeof (id_raw), '\0') != OSSL_RV_OK)) + return 1; + opts.uv.id_hex = id_hex; + break; + case 'c': + opts.comment = optarg; + break; + case 'h': + default: + return usage_compose(comm, NULL, NULL); + } + } + + if (optind < argc) + return usage_compose(comm, "Wrong arguments", NULL); + + if (!opts.otype || !opts.oalg) + return usage_compose(comm, "Missing required arguments", NULL); + + if (!opts.asym && opts.pubkey) + return usage_compose(comm, "Public-Key not supported for algorithm ", opts.oalg); + + if ((strcmp(opts.otype, "uv") == 0) && (!opts.uv.id_hex)) + return usage_compose(comm, "Missing origin arguments (uv)", NULL); + + return compose(&opts); +} + +static int show(struct opts_show *o) +{ + HBKZPC *hbk = NULL; + const char *desc; + BIO *bi = NULL; + int desclen; + int rc = 1; + + if (!o) + goto out; + + bi = (strcmp(o->in, "-") == 0) ? + BIO_new_fp(stdin, BIO_NOCLOSE) : + BIO_new_file(o->in, "r"); + + if (!bi) { + fprintf(stderr, "unable to open %s\n", o->in); + goto out; + } + + hbk = (o->in_der) ? + d2i_HBKZPC_bio(bi, &hbk) : + PEM_read_bio_HBKZPC(bi, &hbk, NULL, NULL); + + if (!hbk) { + fprintf(stderr, "unable to read %s\n", o->in); + goto out; + } + + desc = (const char *)ASN1_STRING_get0_data(hbk->desc); + desclen = ASN1_STRING_length(hbk->desc); + if (!desc || !desclen || + (strncmp(desc, HBKZPC_DER_DESC, desclen) != 0)) { + fprintf(stderr, "%s: unsupported version %s\n", + o->in, desclen ? desc : ""); + goto out; + } + + fprintf(stdout, "%s\n", + ASN1_STRING_get0_data(hbk->uri)); + rc = 0; +out: + HBKZPC_free(hbk); + BIO_free(bi); + return rc; +} + +int main_show(int argc, char * const argv[]) +{ + struct opts_show opts = { + .in_der = false, + }; + int c; + + while(1) { + c = getopt_long(argc, argv, o_show, options_show, NULL); + if (c < 0) + break; + + switch (c) { + case 'i': + opts.in = optarg; + break; + case 'I': + opts.in_der = strcasecmp(optarg, "der") == 0; + break; + case 'h': + default: + return usage_show(comm, NULL, NULL); + } + } + + if (optind < argc) + return usage_show(comm, "Wrong arguments", NULL); + + if (!opts.in) + return usage_show(comm, "Missing required arguments", NULL); + + return show(&opts); +} + +static int usage_base(const char *comm, const char *msg, const char *arg) +{ + if (msg) + fprintf(stderr, "%s%s\n\n", msg, arg ?: ""); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s [OPTIONS] []\n", comm); + fprintf(stderr, "\nOptions:\n"); + fprintf(stderr, " -V, --version Show version\n"); + fprintf(stderr, " -h, --help Show short help\n"); + fprintf(stderr, "\nCommands:\n"); + fprintf(stderr, " compose\n"); + fprintf(stderr, " show\n"); + fprintf(stderr, "\nCommand Options:\n"); + fprintf(stderr, " See command help\n"); + fprintf(stderr, " %s --help\n", comm); + + return 1; +} + +static int version(void) +{ + fprintf(stdout, "zpckey version %d.%d.%d\n", ZPCKEY_VERSION_MAJOR, + ZPCKEY_VERSION_MINOR, ZPCKEY_VERSION_PATCH); + return 0; +} + +int main(int argc, char * const argv[]) +{ + char *cmd; + + comm = basename(argv[0]); + if (argc < 2) { + usage_base(comm, "Missing command", NULL); + return 1; + } + cmd = argv[1]; + + if (strcmp(cmd, "--version") == 0 || + strcmp(cmd, "-V") == 0) + return version(); + else if (strcmp(cmd, "--help") == 0 || + cmd[0] == '-') + return usage_base(comm, NULL, NULL); + else if (strcmp(cmd, "compose") == 0) + return main_compose(argc - 1, &argv[1]); + else if (strcmp(cmd, "show") == 0) + return main_show(argc - 1, &argv[1]); + else + return usage_base(comm, "Command not supported: ", cmd); + + return 0; +} From 4d8591b83bf090bcca56e0d381a146c2ab12e89a Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Mon, 20 Apr 2026 08:36:39 +0200 Subject: [PATCH 36/44] cmake: Integrate zpckey Signed-off-by: Holger Dengler --- CMakeLists.txt | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 552e06d6..06087669 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,6 +312,55 @@ install( DESTINATION ${CMAKE_INSTALL_MANDIR}/man7 ) +########################################################### +# zpckey + +set(ZPCKEY_SOURCES + src/zpckey.c +) + +add_executable(zpckey ${ZPCKEY_SOURCES}) + +target_include_directories( + zpckey + PRIVATE + src + ${OPENSSL_INCLUDE_DIR} + ${CMAKE_BINARY_DIR} +) + +target_link_libraries( + zpckey + PRIVATE + OpenSSL::Crypto + $ + $ +) + +if (BUILD_ASAN) +target_link_options( + zpckey + BEFORE PUBLIC -fsanitize=undefined + PUBLIC -fsanitize=address +) +endif () # BUILD_ASAN + +target_compile_definitions( + zpckey PRIVATE + ZPCKEY_VERSION_MAJOR=${ZPC_VERSION_MAJOR} + ZPCKEY_VERSION_MINOR=${ZPC_VERSION_MINOR} + ZPCKEY_VERSION_PATCH=${ZPC_VERSION_PATCH} +) +install( + TARGETS zpckey + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) + +install( + FILES ${CMAKE_BINARY_DIR}/zpckey.1 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 +) + ########################################################### # Test From e77b6dcee448fb1981ff773367fd7f73d1e1d615 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 15 May 2026 13:47:35 +0200 Subject: [PATCH 37/44] gitignore: Ignore PEM/DER key files As PEM/DER key files are supported now, exclude them from version control by default. Signed-off-by: Holger Dengler --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 215128c4..d69fce66 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,9 @@ .vscode/ +# ignore key-files +*.pem +*.der + # ignore common build directory /build/ From 8fa5056123dcea26bf98b3f20c393be58c30e47d Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sun, 17 May 2026 10:24:09 +0200 Subject: [PATCH 38/44] test: Add tests for zpckey The test script iterates over a list of algorithms and generates the required key files for further tests. It covers test-cases for the tool `zpckey`. The test-case depends on one clear-key for each algorithm. These clear-keys must also exist as related UV retrievable secret. Signed-off-by: Holger Dengler --- test/TEST.md | 7 +++ test/t_ossl_uv_prepkey | 99 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100755 test/t_ossl_uv_prepkey diff --git a/test/TEST.md b/test/TEST.md index 19732f22..cec4116e 100644 --- a/test/TEST.md +++ b/test/TEST.md @@ -1,6 +1,13 @@ test === +t_ossl_*testsuite* +--- +Test suites using `openssl` command line tooling for provider tests. + +internal tests +=== + testlib --- Functions used by multiple test suites. diff --git a/test/t_ossl_uv_prepkey b/test/t_ossl_uv_prepkey new file mode 100755 index 00000000..0ea78158 --- /dev/null +++ b/test/t_ossl_uv_prepkey @@ -0,0 +1,99 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# Copyright contributors to the libzpc project + +OPENSSL_CONF=${OPENSSL_CONF:-"build/openssl.conf"} +OPENSSL=${OPENSSL:-"openssl"} +ZPCKEY=${ZPCKEY:-"build/zpckey"} +ZPC_SEC_DIR=${ZPC_SEC_DIR:-"."} +ZPC_KEY_DIR=${ZPC_KEY_DIR:-"."} +V=${V:-"0"} + +ALGS=( "prime256v1" "secp384r1" "secp521r1" "ED25519" "ED448" ) +ZPCS=( "p-256" "p-384" "p-521" "ed25519" "ed448" ) +CLRS=( "p256" "p384" "p521" "ed25519" "ed448" ) + +prepare_key() { + local alg="$1" + local zpc="$2" + local clr="$3" + local ZPC_PEM="${ZPC_KEY_DIR}/${ZPC}-hbkzpc.pem" + local CLR_PEM="${ZPC_SEC_DIR}/${clr}.key" + local CLR_PUB_PEM="${ZPC_KEY_DIR}/${clr}-pub.pem" + + [ -r "${CLR_PEM}" ] || return 77 + + echo "--- prepare key for ${ALG} ---" + + ${OPENSSL} pkey \ + -in "${CLR_PEM}" \ + -pubout \ + -outform "PEM" \ + -out "${CLR_PUB_PEM}" \ + || return 99 + + + ${ZPCKEY} compose \ + --origin-type "uv" \ + --origin-alg ${alg} \ + --uv-secret-name "${zpc}" \ + --pubkey "${CLR_PUB_PEM}" \ + --comment "TEST" \ + --outform "PEM" \ + --out ${ZPC_PEM} \ + || return 99 + + return 0 +} + +tap13() { + local rc="$1" + local idx="$2" + local text="$3" + local log="$4" + + case ${rc} in + 0) + echo "ok ${idx} - ${text}" + if [ ${V} -eq 1 ]; then + echo " ---" + echo "${log}" + echo " ---" + fi + ;; + 77) + echo "ok ${idx} # SKIP" + ;; + *) + echo "not ok ${idx} - ${text}" + echo " ---" + echo "${log}" + echo " ---" + ;; + esac + + return 0 +} + +main() { + fail=0 + tc=0 + for ((i=0; i<${#ALGS[@]}; i++)); do + local ALG="${ALGS[$i]}" + local ZPC="${ZPCS[$i]}" + local CLR="${CLRS[$i]}" + + tc=$(( ${tc} + 1 )) + + log=$(prepare_key "${ALG}" "${ZPC}" "${CLR}") + rc="$?" + [ ${rc} -ne 0 ] && fail=1 + tap13 "${rc}" "${tc}" "Prepare keys for ${ALG}" "${log}" + done + return ${fail} +} + +if [[ "$0" == "${BASH_SOURCE[0]}" ]]; then + main + exit $? +fi From c0c7a82179f51b7ae120a927f55e290cca3dde24 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sun, 17 May 2026 04:07:59 -0400 Subject: [PATCH 39/44] test: Add tests for key parameters The test script iterated over a list of algorithms and query parameters of the key for each algorithm. The test depends on the keys files, which are created by the `t_ossl_prepkey` test-case. Signed-off-by: Holger Dengler --- test/t_ossl_uv_pkey | 88 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 test/t_ossl_uv_pkey diff --git a/test/t_ossl_uv_pkey b/test/t_ossl_uv_pkey new file mode 100755 index 00000000..4a7cb47f --- /dev/null +++ b/test/t_ossl_uv_pkey @@ -0,0 +1,88 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# Copyright contributors to the libzpc project + +export OPENSSL_CONF=${OPENSSL_CONF:-"build/openssl.conf"} +OPENSSL=${OPENSSL:-"openssl"} +ZPCKEY=${ZPCKEY:-"build/zpckey"} +ZPC_KEY_DIR=${ZPC_KEY_DIR:-"."} +V=${V:-"0"} + +ALGS=( "prime256v1" "secp384r1" "secp521r1" "ED25519" "ED448" ) +ZPCS=( "p-256" "p-384" "p-521" "ed25519" "ed448" ) +CLRS=( "p256" "p384" "p521" "ed25519" "ed448" ) + +tap13() { + local rc="$1" + local idx="$2" + local text="$3" + local log="$4" + + case ${rc} in + 0) + echo "ok ${idx} - ${text}" + if [ ${V} -eq 1 ]; then + echo " ---" + echo "${log}" + echo " ---" + fi + ;; + 77) + echo "ok ${idx} # SKIP" + ;; + *) + echo "not ok ${idx} - ${text}" + echo " ---" + echo "${log}" + echo " ---" + ;; + esac + + return 0 +} + +pkey() { + local key="$1" + + [ -n "${key}" ] || return 77 + + ${OPENSSL} pkey \ + -in ${key} \ + -pubin \ + -text \ + -pubout \ + 2>&1 \ + || return 99 + + return 0 +} + +uv_pkey() { + fail=0 + tc=0 + for ((i=0; i<${#ALGS[@]}; i++)); do + ALG="${ALGS[$i]}" + ZPC="${ZPCS[$i]}" + ZPC_PEM="${ZPC_KEY_DIR}/${ZPC}-hbkzpc.pem" + + tc=$(( ${tc} + 1 )) + log=$(pkey "${ZPC_PEM}") + rc="$?" + [ ${rc} -ne 0 ] && fail=1 + tap13 "${rc}" "${tc}" "Pkey (PEM) for ${ALG}" "${log}" + + tc=$(( ${tc} + 1 )) + ZPC_URI=$(${ZPCKEY} show --in "${ZPC_PEM}") + log=$(pkey "${ZPC_URI}") + rc="$?" + [ ${rc} -ne 0 ] && fail=1 + tap13 "${rc}" "${tc}" "Pkey (URI) for ${ALG}" "${log}" + done + + return ${fail} +} + +if [[ "$0" == "${BASH_SOURCE[0]}" ]]; then + uv_pkey + exit $? +fi From b7c228a9821c980216f1b2a8b74986c7849b3a59 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sun, 17 May 2026 10:27:01 +0200 Subject: [PATCH 40/44] test: Add tests for sign/verify The test script iterates over a list of algorithm and performs the following tests for each algorithm: - sign with zpc-key, verify with zpc-key - sign with zpc-key, verify with clear-key (priv, pub) - sign with clear-key, verify with zpc-key The test depends on the keys files, which are created by the `t_ossl_prepkey` test-case. Signed-off-by: Holger Dengler --- test/t_ossl_uv_sign_verify | 142 +++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100755 test/t_ossl_uv_sign_verify diff --git a/test/t_ossl_uv_sign_verify b/test/t_ossl_uv_sign_verify new file mode 100755 index 00000000..749b38c5 --- /dev/null +++ b/test/t_ossl_uv_sign_verify @@ -0,0 +1,142 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT +# Copyright contributors to the libzpc project + +export OPENSSL_CONF=${OPENSSL_CONF:-"build/openssl.conf"} +OPENSSL=${OPENSSL:-"openssl"} +ZPC_SEC_DIR=${ZPC_SEC_DIR:-"."} +ZPC_KEY_DIR=${ZPC_KEY_DIR:-"."} +V=${V:-"0"} + +ALGS=( "prime256v1" "secp384r1" "secp521r1" "ED25519" "ED448" ) +ZPCS=( "p-256" "p-384" "p-521" "ed25519" "ed448" ) +CLRS=( "p256" "p384" "p521" "ed25519" "ed448" ) + +TTMPDIR=$(mktemp -d) +MSG="${TTMPDIR}/msg" +SIG="${TTMPDIR}/msg.sig" + +tap13() { + local rc="$1" + local idx="$2" + local text="$3" + local log="$4" + + case ${rc} in + 0) + echo "ok ${idx} - ${text}" + if [ ${V} -eq 1 ]; then + echo " ---" + echo "${log}" + echo " ---" + fi + ;; + 77) + echo "ok ${idx} # SKIP" + ;; + *) + echo "not ok ${idx} - ${text}" + echo " ---" + echo "${log}" + echo " ---" + ;; + esac + + return 0 +} + +sign_verify() { + local sigkey="$1" + local verkey="$2" + local prvkey="$3" + local digest="$4" + + [ -r "${sigkey}" ] || return 77 + [ -r "${verkey}" ] || return 77 + + rm -f ${SIG} + + ${OPENSSL} dgst \ + -sign ${sigkey} \ + ${DIGEST} \ + -out "${SIG}" \ + "${MSG}" \ + 2>&1 \ + || return 99 + echo "sign ok" + + ${OPENSSL} dgst \ + -verify ${verkey} \ + ${DIGEST} \ + -signature "${SIG}" \ + "${MSG}" \ + 2>&1 \ + || return 99 + echo "verify ok" + + [ -z "${prvkey}" ] && return 0 + + ${OPENSSL} dgst \ + -prverify ${prvkey} \ + ${DIGEST} \ + -signature "${SIG}" \ + "${MSG}" \ + 2>&1 \ + || return 99 + echo "prverify ok" + + return 0 +} + +uv_sign_verify() { + dd \ + if=/dev/random \ + of=${MSG} \ + bs=1K \ + count=1K \ + > /dev/null 2>&1 \ + || return 1 + + fail=0 + tc=0 + for ((i=0; i<${#ALGS[@]}; i++)); do + ALG="${ALGS[$i]}" + ZPC="${ZPCS[$i]}" + CLR="${CLRS[$i]}" + ZPC_PEM="${ZPC_KEY_DIR}/${ZPC}-hbkzpc.pem" + CLR_PEM="${ZPC_SEC_DIR}/${CLR}.key" + CLR_PUB_PEM="${ZPC_KEY_DIR}/${CLR}-pub.pem" + + if $(echo "${ALG}" | grep -iq "^ed") ; then + DIGEST="" + else + DIGEST="-sha256" + fi + + tc=$(( ${tc} + 1 )) + log=$(sign_verify "${ZPC_PEM}" "${ZPC_PEM}" "${ZPC_PEM}" "${DIGEST}") + rc="$?" + [ ${rc} -ne 0 ] && fail=1 + tap13 "${rc}" "${tc}" "Sign/verify (zpc/zpc) for ${ALG}" "${log}" + + tc=$(( ${tc} + 1 )) + log=$(sign_verify "${ZPC_PEM}" "${CLR_PUB_PEM}" "${CLR_PEM}" "${DIGEST}") + rc="$?" + [ ${rc} -ne 0 ] && fail=1 + tap13 "${rc}" "${tc}" "Sign/verify (zpc/clr) for ${ALG}" "${log}" + + tc=$(( ${tc} + 1 )) + log=$(sign_verify "${CLR_PEM}" "${ZPC_PEM}" "${ZPC_PEM}" "${DIGEST}") + rc="$?" + [ ${rc} -ne 0 ] && fail=1 + tap13 "${rc}" "${tc}" "Sign/verify (clr/zpc) for ${ALG}" "${log}" + done + + rm -rf ${TTMPDIR} + return ${fail} +} + +if [[ "$0" == "${BASH_SOURCE[0]}" ]]; then + uv_sign_verify + exit $? +fi From 82d30b9d663c53b69ddf33c4f3246faed93ed984 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Sun, 17 May 2026 11:58:31 +0200 Subject: [PATCH 41/44] cmake: Introduce platform-independent targets The introduction of platform-independent targets allows to build parts of the project for non-s390x platforms. This is required at least for the tool `zpckey`. Signed-off-by: Holger Dengler --- CMakeLists.txt | 58 +++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06087669..cbbb3c99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,33 @@ project(${ZPC_NAME} LANGUAGES CXX C ASM ) +set(THREADS_PREFER_PTHREAD_FLAG TRUE) +find_package(Threads + REQUIRED +) + +find_package(json-c + REQUIRED +) + +find_package(OpenSSL + 3.0.7 + REQUIRED +) + +find_package(PkgConfig + QUIET +) + +add_definitions( + -D_GNU_SOURCE +) + +########################################################### +# zpc + +if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL s390x) + set(ZPC_HEADERS include/zpc/error.h include/zpc/aes_key.h @@ -100,28 +127,6 @@ set(ZPC_SOURCES src/zkey/lib/util_prg.c ) -set(THREADS_PREFER_PTHREAD_FLAG TRUE) -find_package(Threads - REQUIRED -) - -find_package(json-c - REQUIRED -) - -find_package(OpenSSL - 3.0.7 - REQUIRED -) - -find_package(PkgConfig - QUIET -) - -add_definitions( - -D_GNU_SOURCE -) - set(ZPC_LIBS Threads::Threads ${CMAKE_DL_LIBS} ) @@ -139,6 +144,9 @@ target_compile_definitions( ZPC_VERSION_MINOR=${ZPC_VERSION_MINOR} ZPC_VERSION_PATCH=${ZPC_VERSION_PATCH} ) + +endif() # CMAKE_SYSTEM_PROCESSOR == s390x + include(GNUInstallDirs) ########################################################### @@ -214,6 +222,8 @@ add_dependencies( ########################################################### # zpcprovider +if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL s390x) + set(ZPCPROVIDER_SOURCES src/provider.c src/object.c @@ -312,6 +322,8 @@ install( DESTINATION ${CMAKE_INSTALL_MANDIR}/man7 ) +endif() # CMAKE_SYSTEM_PROCESSOR == s390x + ########################################################### # zpckey @@ -367,6 +379,7 @@ install( option(BUILD_TEST OFF) option(BUILD_INTERNAL_TEST OFF) +if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL s390x) if (BUILD_INTERNAL_TEST) enable_testing() @@ -649,6 +662,7 @@ include(GoogleTest) gtest_discover_tests(runtest) endif () # BUILD_INTERNAL_TEST +endif () # CMAKE_SYSTEM_PROCESSOR == s390x ########################################################### # doc From a0579543a91138c5adfee56c7c58c32bae0a6b63 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 29 May 2026 17:22:34 +0200 Subject: [PATCH 42/44] test: Add test tool to compare provider EVP_PKEY objects The tool pkeycmp takes two provider key files (PEM or DER) and compares them. Signed-off-by: Holger Dengler --- test/pkeycmp.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 test/pkeycmp.c diff --git a/test/pkeycmp.c b/test/pkeycmp.c new file mode 100644 index 00000000..2d46e4d1 --- /dev/null +++ b/test/pkeycmp.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include +#include + +static const char *comm = NULL; + +static EVP_PKEY *pkey_read(const char *path) +{ + BIO *bin = BIO_new_file(path, "r"); + EVP_PKEY *pkey = NULL; + + if (!bin) + goto out; + + pkey = PEM_read_bio_PrivateKey(bin, &pkey, NULL, NULL); + if (pkey) + goto out; + + if (BIO_reset(bin) > 0) + goto out; + pkey = PEM_read_bio_PUBKEY(bin, &pkey, NULL, NULL); +out: + BIO_free(bin); + return pkey; +} + +static void usage(const char *msg, const char *arg) +{ + if (msg) + fprintf(stdout, "%s%s\n", msg, arg ?: ""); + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " %s --in1|-1 --in2|-2 [--help|-h]\n", comm); + fprintf(stderr, " -1, --in1 First input file\n"); + fprintf(stderr, " -2, --in2 Second input file\n"); + fprintf(stderr, " -h, --help Show short help\n"); +} + +int main(int argc, char *argv[]) +{ + static const struct option long_options[] = { + { "in1", required_argument, NULL, '1' }, + { "in2", required_argument, NULL, '2' }, + { "quiet", no_argument, NULL, 'q' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + static const char options[] = "1:2:qh"; + EVP_PKEY *pkey1 = NULL, *pkey2 = NULL; + const char *in1 = NULL, *in2 = NULL; + bool quiet = false; + int opt, rc = 1; + + comm = argv[0]; + + while ((opt = getopt_long(argc, argv, options, long_options, NULL)) != -1) { + switch (opt) { + case '1': + in1 = optarg; + break; + case '2': + in2 = optarg; + break; + case 'q': + quiet = true; + break; + case 'h': + usage(NULL, NULL); + goto out; + default: + usage("Unknown argument", optarg); + goto out; + } + } + + if (optind < argc) { + usage("Wrong arguments", NULL); + goto out; + } + + if (!in1 || !in2) { + usage("Missing arguments", NULL); + goto out; + } + + if (!(pkey1 = pkey_read(in1))) { + if (!quiet) { + ERR_print_errors_fp(stderr); + fprintf(stderr, "Unable to read EVP_PKEY from %s\n", in1); + } + goto out; + } + + if (!(pkey2 = pkey_read(in2))) { + if (!quiet) { + ERR_print_errors_fp(stderr); + fprintf(stderr, "Unable to read EVP_PKEY from %s\n", in2); + } + goto out; + } + + rc = (EVP_PKEY_eq(pkey1, pkey2) == 1) ? 0 : 1; + if (!quiet) { + ERR_print_errors_fp(stderr); + fprintf(stdout, "%s\n", !rc ? "match" : "no match"); + } +out: + EVP_PKEY_free(pkey1); + EVP_PKEY_free(pkey2); + return rc; +} From 891061c5cd75ca3b4cd9b381d2eead6fe3db8a95 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Fri, 29 May 2026 17:24:04 +0200 Subject: [PATCH 43/44] cmake: Integrate test tool pkeycmp Signed-off-by: Holger Dengler --- CMakeLists.txt | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbbb3c99..4ed56aeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -379,11 +379,47 @@ install( option(BUILD_TEST OFF) option(BUILD_INTERNAL_TEST OFF) -if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL s390x) +if (BUILD_TEST) + +enable_testing() + +set (PKEYCMP_TEST_SOURCES + test/pkeycmp.c +) + +add_executable( + pkeycmp + ${PKEYCMP_TEST_SOURCES} +) + +target_include_directories( + pkeycmp + PRIVATE + src + ${OPENSSL_INCLUDE_DIR} +) + +target_link_libraries( + pkeycmp + PRIVATE + OpenSSL::Crypto +) + +if (BUILD_ASAN) +target_link_options( + pkeycmp + BEFORE PUBLIC -fsanitize=undefined + PUBLIC -fsanitize=address +) +endif () # BUILD_ASAN + +endif () # BUILD_TEST + if (BUILD_INTERNAL_TEST) enable_testing() +if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL s390x) set(GTEST_GIT https://github.com/google/googletest.git ) @@ -661,8 +697,8 @@ target_include_directories(runtest PRIVATE include src ${GTEST_INCLUDE_DIR}) include(GoogleTest) gtest_discover_tests(runtest) -endif () # BUILD_INTERNAL_TEST endif () # CMAKE_SYSTEM_PROCESSOR == s390x +endif () # BUILD_INTERNAL_TEST ########################################################### # doc From 83b122d6ef006990e4afb2fe65521909846d5d5d Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 20 May 2026 13:59:20 +0200 Subject: [PATCH 44/44] travis: Enable multi-arch travis build As the project supports building on non-s390x platforms, enable the multi-arch build in travis. Signed-off-by: Holger Dengler --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ca2f97d1..f029fcc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ -arch: s390x +arch: + - s390x + - amd64 + - arm64 os: linux dist: noble language: c