diff --git a/.travis.yml b/.travis.yml index b38836db..6df1abbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,29 @@ arch: s390x os: linux -dist: jammy -language: cpp +dist: noble +language: c sudo: required -env: | - CFLAGS="-O3 -Wextra -Wextra -Werror" - CXXFLAGS="-O3 -Wextra -Wextra -Werror" + compiler: - gcc - clang before_install: - sudo apt-get update -qq - - sudo apt-get install cmake libjson-c-dev + - sudo apt-get install -y cmake libjson-c-dev + +before_script: + - git clone https://github.com/openssl/openssl.git + - pushd openssl + - git checkout --track origin/openssl-3.5 + - ./config -w 2> >(tee) + - make -s -j4 2> >(tee) + - export OPENSSL_DIR=$(pwd) + - export PATH=${OPENSSL_DIR}/apps/:${PATH} + - export LD_LIBRARY_PATH=${OPENSSL_DIR}:${LD_LIBRARY_PATH} + - popd script: - set -o pipefail - - mkdir build 2> >(tee) && cd build 2> >(tee) - - cmake -DBUILD_TEST=ON .. 2> >(tee) - - make 2> >(tee) + - cmake -B build -S . -DCMAKE_INCLUDE_PATH=${OPENSSL_DIR} -DCMAKE_LIBRARY_PATH=${OPENSSL_DIR} 2> >(tee) + - cmake --build build --target zpc 2> >(tee) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ffb978..c4173519 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,25 +95,24 @@ find_package(json-c REQUIRED ) +find_package(OpenSSL + 3.5.0 + REQUIRED +) + add_definitions( -D_GNU_SOURCE ) set(ZPC_LIBS - Threads::Threads ${CMAKE_DL_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,35 +121,202 @@ 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/ +########################################################### +# 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} +) + +########################################################### +# 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 + +set(ZPCPROVIDER_SOURCES + src/provider.c + src/object.c + src/map.c + src/store.c + src/keymgmt.c + src/algid.c + src/signature.c + src/tls.c + src/decoder.c +) + +add_library( + zpcprovider MODULE + ${ZPCPROVIDER_SOURCES} +) + +set_target_properties( + zpcprovider + PROPERTIES + PREFIX "" +) + +target_include_directories( + zpcprovider + PRIVATE + src + include + ${OPENSSL_INCLUDE_DIR} +) + +target_link_libraries( + zpcprovider + PRIVATE + OpenSSL::Crypto + $ + $ + $ +) + +target_link_options( + zpcprovider + PRIVATE + -Wl,--version-script=${CMAKE_SOURCE_DIR}/zpcprovider.map ) install( - FILES ${CMAKE_BINARY_DIR}/libzpc.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + TARGETS zpcprovider + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBEXECDIR} ) +set(OPENSSL_CONF + ${CMAKE_BINARY_DIR}/openssl.cnf +) +set(OPENSSL_CONF_IN + ${CMAKE_SOURCE_DIR}/openssl.cnf.in +) +set(ZPCPROVIDER_MODULE + ${CMAKE_BINARY_DIR}/zpcprovider.so +) +configure_file(${OPENSSL_CONF_IN} ${OPENSSL_CONF} @ONLY) + +# install( +# FILES ${CMAKE_SOURCE_DIR}/man/zpcprovider.cnf.5 +# DESTINATION ${CMAKE_INSTALL_MANDIR}/man5 +# ) +# +# install( +# FILES ${CMAKE_SOURCE_DIR}/man/zpcprovider.7 +# DESTINATION ${CMAKE_INSTALL_MANDIR}/man7 +# ) + ########################################################### # Test option(BUILD_TEST OFF) +option(BUILD_INTERNAL_TEST OFF) if (BUILD_TEST) enable_testing() -set(GTEST_URL - https://github.com/google/googletest/archive/refs/tags/release-1.11.0.zip +set (ZPCPROVIDER_TEST_SOURCES + test/t_provider.c +) + +add_executable( + runprovidertest + ${ZPCPROVIDER_TEST_SOURCES} +) + +add_dependencies( + runprovidertest + zpcprovider +) + +target_include_directories( + runprovidertest + PRIVATE + src + ${OPENSSL_INCLUDE_DIR} +) + +target_link_libraries( + runprovidertest + PRIVATE + OpenSSL::Crypto + $ $ +) + +endif () # BUILD_TEST + +if (BUILD_INTERNAL_TEST) + +enable_testing() + +set(GTEST_GIT + https://github.com/google/googletest.git ) -set(GTEST_SHA256 - 353571c2440176ded91c2de6d6cd88ddd41401d14692ec1f99e35d013feda55a +set(GTEST_TAG + release-1.12.1 ) set(WYCHEPROOF_GIT @@ -225,19 +391,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) @@ -255,11 +421,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) @@ -277,11 +443,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) @@ -327,20 +493,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) @@ -355,12 +521,12 @@ ExternalProject_Add(wycheproof ) ExternalProject_Add(gtest - URL ${GTEST_URL} - URL_HASH SHA256=${GTEST_SHA256} - PREFIX ${CMAKE_BINARY_DIR}/gtest - INSTALL_COMMAND "" - TEST_COMMAND "" - CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" + 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}" ) ExternalProject_Get_Property(gtest SOURCE_DIR BINARY_DIR) set(GTEST_INCLUDE_DIR @@ -375,7 +541,7 @@ set(ZPC_TEST_LIBS ${GTEST_LIB_DIR}/libgtest.a Threads::Threads json-c::json-c - zpc + $ ) set(ZPC_TEST_SOURCES @@ -423,7 +589,7 @@ target_include_directories(runtest PRIVATE include src ${GTEST_INCLUDE_DIR}) include(GoogleTest) gtest_discover_tests(runtest) -endif () +endif () # BUILD_INTERNAL_TEST ########################################################### # doc diff --git a/misc/dbg/decoder.gdb b/misc/dbg/decoder.gdb new file mode 100644 index 00000000..c2290046 --- /dev/null +++ b/misc/dbg/decoder.gdb @@ -0,0 +1,7 @@ +set breakpoint pending on +break dec_newctx +break dec_freectx +break dec_pem_der_decode +break dec_der_ec_decode +break dec_der_ed25519_decode +break dec_der_ed448_decode diff --git a/misc/dbg/kmgmt.gdb b/misc/dbg/kmgmt.gdb new file mode 100644 index 00000000..615539a1 --- /dev/null +++ b/misc/dbg/kmgmt.gdb @@ -0,0 +1,17 @@ +set breakpoint pending on +break kmgmt_new +break kmgmt_free +break kmgmt_load +break kmgmt_has +break kmgmt_export_types_ex +break kmgmt_set_params +break kmgmt_settable_params +break ec_get_params +break ec_export +break ed_get_params +break ed_export +break ec_gettable_params +break ed_gettable_params +break ec_query_operation_name +break ed25519_query_operation_name +break ed448_query_operation_name diff --git a/misc/dbg/provider.gdb b/misc/dbg/provider.gdb new file mode 100644 index 00000000..a59aa49e --- /dev/null +++ b/misc/dbg/provider.gdb @@ -0,0 +1,8 @@ +set breakpoint pending on +break prov_init +break prov_teardown +break prov_gettable_params +break prov_get_params +break prov_query_operation +break prov_get_capabilities +break prov_get_reason_strings diff --git a/misc/dbg/signature.gdb b/misc/dbg/signature.gdb new file mode 100644 index 00000000..efd55b46 --- /dev/null +++ b/misc/dbg/signature.gdb @@ -0,0 +1,22 @@ +set breakpoint pending on +break sig_digest_sign_final +break sig_digest_sign_init +break sig_digest_update +break sig_digest_verify_final +break sig_digest_verify_init +break sig_freectx +break sig_sign +break sig_sign_init +break sig_verify +break sig_verify_init +break ecdsa_get_ctx_params +break ecdsa_gettable_ctx_params +break ecdsa_set_ctx_params +break ecdsa_settable_ctx_params +break eddsa_get_ctx_params +break eddsa_gettable_ctx_params +break eddsa_set_ctx_params +break eddsa_settable_ctx_params +break ecdsa_newctx +break ed25519_newctx +break ed448_newctx diff --git a/misc/dbg/store.gdb b/misc/dbg/store.gdb new file mode 100644 index 00000000..bf37da9c --- /dev/null +++ b/misc/dbg/store.gdb @@ -0,0 +1,11 @@ +set breakpoint pending on +break store_ctx_init +break store_ctx_free +break store_ctx_expect +break store_open +break store_open_ex +break store_load +break store_eof +break store_close +break store_set_ctx_params +break store_settable_ctx_params diff --git a/openssl.cnf.in b/openssl.cnf.in new file mode 100644 index 00000000..8d3edc09 --- /dev/null +++ b/openssl.cnf.in @@ -0,0 +1,28 @@ +HOME = . + +# Use this in order to automatically load providers. +openssl_conf = openssl_init + +config_diagnostics = 1 + +[openssl_init] +providers = provider_sect +alg_section = evp_properties + +[provider_sect] +default = default_sect +base = base_sect +hbkzpc = hbkzpc_sect + +[evp_properties] + +[base_sect] +activate = 1 + +[default_sect] +activate = 1 + +[hbkzpc_sect] +module = @ZPCPROVIDER_MODULE@ +identity = hbkzpc +activate = 1 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 */ diff --git a/src/asn1.c b/src/asn1.c new file mode 100644 index 00000000..ca0749ff --- /dev/null +++ b/src/asn1.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include "asn1.h" + +ASN1_SEQUENCE(ZPCHBK) = { + ASN1_EXP(ZPCHBK, desc, ASN1_VISIBLESTRING, 0), + ASN1_EXP(ZPCHBK, uri, ASN1_UTF8STRING, 1), +} ASN1_SEQUENCE_END(ZPCHBK); + +#include "asn1_gen.c" + +int i2d_ZPCHBK_bio(BIO *bp, const ZPCHBK *hbkp) +{ + return ASN1_i2d_bio_of(ZPCHBK, i2d_ZPCHBK, bp, hbkp); +} + +ZPCHBK *d2i_ZPCHBK_bio(BIO *bp, ZPCHBK **hbkpp) +{ + return ASN1_d2i_bio_of(ZPCHBK, ZPCHBK_new, d2i_ZPCHBK, + bp, hbkpp); +} diff --git a/src/asn1.h b/src/asn1.h new file mode 100644 index 00000000..21e91b4b --- /dev/null +++ b/src/asn1.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _ASN1_H +#define _ASN1_H + +#include +#include + +#define HBKZPC_PEM_STRING "ZPC HARDWARE BACKED KEY" +#define HBKZPC_DER_DESC "HBKZPC Provider URI v1.0" + +struct zpc_hardware_backed_key_sequence_st { + ASN1_VISIBLESTRING *desc; + ASN1_UTF8STRING *uri; +}; +typedef struct zpc_hardware_backed_key_sequence_st ZPCHBK; +DECLARE_ASN1_FUNCTIONS(ZPCHBK) + +int i2d_ZPCHBK_bio(BIO *bp, const ZPCHBK *hbkp); +ZPCHBK *d2i_ZPCHBK_bio(BIO *bp, ZPCHBK **hbkpp); + +DECLARE_PEM_write_bio(ZPCHBK, ZPCHBK) +DECLARE_PEM_read_bio(ZPCHBK, ZPCHBK) +#endif /* _ASN1_H */ diff --git a/src/asn1_gen.c.in b/src/asn1_gen.c.in new file mode 100644 index 00000000..1f719c08 --- /dev/null +++ b/src/asn1_gen.c.in @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include + +BEGIN: +IMPLEMENT_ASN1_FUNCTIONS(ZPCHBK) +IMPLEMENT_PEM_write_bio(ZPCHBK, ZPCHBK, HBKZPC_PEM_STRING, ZPCHBK) +IMPLEMENT_PEM_read_bio(ZPCHBK, ZPCHBK, HBKZPC_PEM_STRING, ZPCHBK) diff --git a/src/decoder.c b/src/decoder.c new file mode 100644 index 00000000..8d900fd0 --- /dev/null +++ b/src/decoder.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include + +#include "store_local.h" + +#include "provider.h" +#include "decoder.h" +#include "ossl.h" +#include "asn1.h" +#include "uri.h" +#include "map.h" + +#define DECODER_DER_STRUCTURE "zpchbk" +#define DECODER_PROP_PEM PROV_PROP",input=pem" +#define DECODER_PROP_DER PROV_PROP",input=der,structure="DECODER_DER_STRUCTURE + +#define DECODER_CARRYON OSSL_RV_TRUE +#define DECODER_STOP OSSL_RV_FALSE + +struct decoder_ctx { + struct provider_ctx *pctx; +}; + +static void *dec_newctx(void *vpctx) +{ + struct provider_ctx *pctx = (struct provider_ctx *)vpctx; + struct decoder_ctx *dctx; + + if (!pctx) + return NULL; + + dctx = OPENSSL_zalloc(sizeof(struct decoder_ctx)); + if (!dctx) + return NULL; + + dctx->pctx = pctx; + return dctx; +} + +static void dec_freectx(void *vdctx) +{ + OPENSSL_free(vdctx); +} + +static int dec_pem_der_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection __unused, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb __unused, + void *cbarg __unused) +{ + char *label = NULL, *header = NULL; + struct decoder_ctx *dctx = vdctx; + unsigned char *data = NULL; + int rc = DECODER_CARRYON; + OSSL_PARAM params[3]; + long datalen; + BIO *bin; + + bin = BIO_new_from_core_bio(dctx->pctx->libctx, in); + if (!bin) + goto out; + + if (PEM_read_bio(bin, &label, &header, &data, &datalen) != OSSL_RV_OK || + OPENSSL_strcasecmp(label, HBKZPC_PEM_STRING) != 0) + goto out; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, + data, datalen); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, + DECODER_DER_STRUCTURE, 0); + params[2] = OSSL_PARAM_construct_end(); + rc = data_cb(params, data_cbarg); +out: + OPENSSL_free(header); + OPENSSL_free(label); + OPENSSL_free(data); + BIO_free(bin); + + return rc; +} + +static int dec_der_decode(struct decoder_ctx *dctx, OSSL_CORE_BIO *in, + int selection, const char *data_type, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + struct parsed_uri *puri = NULL; + const char *uri_data_type; + bool public_only = false; + int rv = DECODER_CARRYON; + ZPCHBK *hbk = NULL; + BIO *bin; + + bin = BIO_new_from_core_bio(dctx->pctx->libctx, in); + if (!bin) + goto out; + + if (!d2i_ZPCHBK_bio(bin, &hbk)) + goto out; + + puri = parsed_uri_new((const char *)ASN1_STRING_get0_data(hbk->uri)); + if (!puri) + goto out; + + uri_data_type = alg2data_type(puri->origin_alg.value); + + if (OPENSSL_strcasecmp(data_type, uri_data_type) != 0) + goto out; + + public_only = selection && !(selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY); + + rv = store_load_uri(dctx->pctx, puri, public_only, + data_cb, data_cbarg, cb, cbarg); +out: + parsed_uri_free(puri); + OPENSSL_free(hbk); + BIO_free(bin); + return rv; +} + +static int dec_der_ec_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return dec_der_decode(vdctx, in, selection, PROV_NAME_EC, + data_cb, data_cbarg, cb, cbarg); +} + +static int dec_der_ed25519_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return dec_der_decode(vdctx, in, selection, PROV_NAME_ED25519, + data_cb, data_cbarg, cb, cbarg); +} + +static int dec_der_ed448_decode(void *vdctx, OSSL_CORE_BIO *in, + int selection, + OSSL_CALLBACK *data_cb, void *data_cbarg, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg) +{ + return dec_der_decode(vdctx, in, selection, PROV_NAME_ED448, + data_cb, data_cbarg, cb, cbarg); +} + +static const OSSL_DISPATCH decoder_der_ec_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_der_ec_decode), + DISPATCH_END, +}; + +static const OSSL_DISPATCH decoder_der_ed25519_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_der_ed25519_decode), + DISPATCH_END, +}; + +static const OSSL_DISPATCH decoder_der_ed448_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_der_ed448_decode), + DISPATCH_END, +}; + +static const OSSL_DISPATCH decoder_pem_der_functions[] = { + DISPATCH_DEFN(DECODER, NEWCTX, dec_newctx), + DISPATCH_DEFN(DECODER, FREECTX, dec_freectx), + DISPATCH_DEFN(DECODER, DECODE, dec_pem_der_decode), + DISPATCH_END, +}; + +const OSSL_ALGORITHM decoder_ops[] = { + ALGORITHM_DEFN("DER", DECODER_PROP_PEM, decoder_pem_der_functions, NULL), + ALGORITHM_DEFN(PROV_NAME_EC, DECODER_PROP_DER, decoder_der_ec_functions, NULL), + ALGORITHM_DEFN(PROV_NAME_ED25519, DECODER_PROP_DER, decoder_der_ed25519_functions, NULL), + ALGORITHM_DEFN(PROV_NAME_ED448, DECODER_PROP_DER, decoder_der_ed448_functions, NULL), + ALGORITHM_END, +}; diff --git a/src/decoder.h b/src/decoder.h new file mode 100644 index 00000000..3c400169 --- /dev/null +++ b/src/decoder.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _DECODER_H +#define _DECODER_H + +#include + +extern const OSSL_ALGORITHM decoder_ops[]; + +#endif /* _DECODER_H */ diff --git a/src/keymgmt.c b/src/keymgmt.c new file mode 100644 index 00000000..1a3759a0 --- /dev/null +++ b/src/keymgmt.c @@ -0,0 +1,606 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include +#include +#include +#include + +#include "provider.h" +#include "object.h" +#include "ossl.h" +#include "map.h" +#include "zpc/ecc_key.h" +#include "zpc/error.h" + +static struct { + const char *str; + int type; +} type_map[] = { + { .str = "uv", .type = ZPC_EC_KEY_TYPE_PVSECRET, }, + { .str = "cca", .type = ZPC_EC_KEY_TYPE_CCA, }, + { .str = "ep11", .type = ZPC_EC_KEY_TYPE_EP11, }, + { 0 }, +}; + +static int str2type(const char *str) +{ + for (int i = 0; type_map[i].str; i++) { + if (OPENSSL_strcasecmp(type_map[i].str, str) == 0) + return type_map[i].type; + } + return 0; +} + +static struct { + const char *alg; + int bits; + int secbits; + int sigsz; +} alg_param_map[] = { + { + .alg = SN_X9_62_prime256v1, + .bits = 256, .secbits = 128, .sigsz = 64 + 8, + }, { + .alg = SN_secp384r1, + .bits = 384, .secbits = 192, .sigsz = 96 + 8, + }, { + .alg = SN_secp521r1, + .bits = 521, .secbits = 256, .sigsz = 132 + 8 + }, { + .alg = SN_ED25519, + .bits = 256, .secbits = 128, .sigsz = 64 + }, { + .alg = SN_ED448, + .bits = 456, .secbits = 224, .sigsz = 114}, + { 0 }, +}; + +static int alg2param(const char *alg, int *bits, int *secbits, int *sigsz) +{ + int i; + + if (!alg) + return OSSL_RV_ERR; + + for (i = 0; alg_param_map[i].alg; i++) { + if (OPENSSL_strcasecmp(alg_param_map[i].alg, alg) == 0) + goto found; + } + /* not found */ + return OSSL_RV_ERR; +found: + if (bits) + *bits = alg_param_map[i].bits; + if (secbits) + *secbits = alg_param_map[i].secbits; + if (sigsz) + *sigsz = alg_param_map[i].sigsz; + + return OSSL_RV_OK; +} + +static int zpc_key_update(struct obj *obj) +{ + struct zpc_ec_key *key = NULL; + int rc; + + if ((rc = zpc_ec_key_alloc(&key))) + goto err; + + if ((rc = zpc_ec_key_set_type(key, str2type(obj->origin_type)))) + goto err; + + if ((rc = zpc_ec_key_set_curve(key, obj_key_curve(obj)))) + goto err; + + if ((rc = zpc_ec_key_import(key, obj->origin_blob.p, + obj->origin_blob.plen))) + goto err; + + if (strcmp(obj->origin_type, "uv") == 0 && + obj->origin_pubkey.p) { + /* uv only: import pubkey */ + if ((rc = zpc_ec_key_import_clear(key, obj->origin_pubkey.p, + obj->origin_pubkey.plen, + NULL, 0))) + goto err; + } else { + if (obj->mkvp && (rc = zpc_ec_key_set_mkvp(key, obj->mkvp))) + goto err; + /* TODO: apqns */ + } + + obj->ec_key = key; + return OSSL_RV_OK; +err: + PROV_ERR_raise(obj->pctx, rc); + zpc_ec_key_free(&key); + return OSSL_RV_ERR; +} + +static int ec_pub_uncomp(unsigned char *raw, size_t rawlen, + unsigned char **pub, size_t *publen) +{ + unsigned char *p; + size_t plen; + + if (!raw || !rawlen) + return OSSL_RV_ERR; + + plen = rawlen + 1; + p = OPENSSL_malloc(plen); + if (!p) + return OSSL_RV_ERR; + + p[0] = POINT_CONVERSION_UNCOMPRESSED; + memcpy(&p[1], raw, rawlen); + + if(pub) + *pub = p; + if (publen) + *publen = plen; + + return OSSL_RV_OK; +} + +static int ec_enc_pubkey_param(struct obj *obj, OSSL_PARAM *p) +{ + unsigned char *pub = NULL; + size_t publen; + int rv; + + if (!obj || !obj->origin_pubkey.p) + return OSSL_RV_ERR; + + if (ec_pub_uncomp(obj->origin_pubkey.p, obj->origin_pubkey.plen, + &pub, &publen) != OSSL_RV_OK) + return OSSL_RV_ERR; + + rv = OSSL_PARAM_set_octet_string(p, pub, publen); + OPENSSL_free(pub); + return rv; +} + +enum ec_coord { + EC_X, + EC_Y, +}; + +static int ec_pubkey_coord_param(struct obj *obj, enum ec_coord coord, + OSSL_PARAM *p) +{ + const unsigned char *raw; + size_t rawlen; + BIGNUM *c; + int rv; + + if (!obj || + !obj->origin_pubkey.p) + return OSSL_RV_ERR; + + rawlen = obj->origin_pubkey.plen / 2; + switch (coord) { + case EC_X: + raw = obj->origin_pubkey.p; + break; + case EC_Y: + raw = obj->origin_pubkey.p + rawlen; + break; + default: + return OSSL_RV_ERR; + } + + if (!(c = BN_bin2bn(raw, rawlen, NULL))) + return OSSL_RV_ERR; + + rv = OSSL_PARAM_set_BN(p, c); + BN_free(c); + return rv; +} + +static int kmgmt_get_params(struct obj *obj, OSSL_PARAM params[]) +{ + int bits, secbits, sigsz; + OSSL_PARAM *p; + + if (!obj || + alg2param(obj->origin_alg, &bits, &secbits, &sigsz) != OSSL_RV_OK) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS); + if (p) { + if (OSSL_PARAM_set_int(p, bits) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS); + if (p) { + if (OSSL_PARAM_set_int(p, secbits) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE); + if (p) { + if (OSSL_PARAM_set_int(p, sigsz) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_GROUP_NAME); + if (p) { + if(OSSL_PARAM_set_utf8_string(p, obj->origin_alg) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static int kmgmt_export(struct obj *obj, int selection, + OSSL_CALLBACK *param_cb, void *cbarg, + bool raw) +{ + int bits, secbits, sigsz, rv = OSSL_RV_ERR; + unsigned char *pub, *p = NULL; + OSSL_PARAM_BLD *param_bld; + OSSL_PARAM *params = NULL; + size_t publen; + + if (!obj || + (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) + return OSSL_RV_ERR; + + if (alg2param(obj->origin_alg, &bits, &secbits, &sigsz) != OSSL_RV_OK) + goto out; + + if (!(param_bld = OSSL_PARAM_BLD_new())) + goto out; + + if (raw) { + publen = obj->origin_pubkey.plen; + pub = obj->origin_pubkey.p; + } else { + if (ec_pub_uncomp(obj->origin_pubkey.p, + obj->origin_pubkey.plen, + &p, &publen) != OSSL_RV_OK) + goto out; + pub = p; + } + + if (selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) { + if (OSSL_PARAM_BLD_push_utf8_string(param_bld, OSSL_PKEY_PARAM_GROUP_NAME, + obj->origin_alg, strlen(obj->origin_alg)) != OSSL_RV_OK) + goto out; + + if (OSSL_PARAM_BLD_push_int(param_bld, OSSL_PKEY_PARAM_BITS, bits) != OSSL_RV_OK) + goto out; + + if (OSSL_PARAM_BLD_push_int(param_bld, OSSL_PKEY_PARAM_SECURITY_BITS, secbits) != OSSL_RV_OK) + goto out; + + if (OSSL_PARAM_BLD_push_int(param_bld, OSSL_PKEY_PARAM_MAX_SIZE, sigsz) != OSSL_RV_OK) + goto out; + } + + if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { + if (OSSL_PARAM_BLD_push_octet_string(param_bld, OSSL_PKEY_PARAM_PUB_KEY, + pub, publen) != OSSL_RV_OK) + goto out; + } + + if (!(params = OSSL_PARAM_BLD_to_param(param_bld))) + goto out; + + rv = param_cb(params, cbarg); +out: + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(param_bld); + return rv; +} + +#define DECL_KMGMT_FN(tname, name) DECL_DISPATCH_FUNC(keymgmt, tname, name) +DECL_KMGMT_FN(new, kmgmt_new); +DECL_KMGMT_FN(free, kmgmt_free); +DECL_KMGMT_FN(load, kmgmt_load); +DECL_KMGMT_FN(has, kmgmt_has); +DECL_KMGMT_FN(export, ec_export); +DECL_KMGMT_FN(export, ed_export); +DECL_KMGMT_FN(export_types_ex, kmgmt_export_types_ex); +DECL_KMGMT_FN(query_operation_name, ec_query_operation_name); +DECL_KMGMT_FN(query_operation_name, ed25519_query_operation_name); +DECL_KMGMT_FN(query_operation_name, ed448_query_operation_name); +DECL_KMGMT_FN(gettable_params, ec_gettable_params); +DECL_KMGMT_FN(gettable_params, ed_gettable_params); +DECL_KMGMT_FN(get_params, ec_get_params); +DECL_KMGMT_FN(get_params, ed_get_params); +DECL_KMGMT_FN(settable_params, kmgmt_settable_params); +DECL_KMGMT_FN(set_params, kmgmt_set_params); +#undef DECL_SKMGMT_FN + +static void *kmgmt_new(void *provctx) +{ + return obj_new((struct provider_ctx *)provctx); +} + +static void kmgmt_free(void *keydata) +{ + struct obj *obj = (struct obj *)keydata; + + zpc_ec_key_free(&obj->ec_key); + obj_free(obj); +} + +static void *kmgmt_load(const void *reference, size_t reference_sz) +{ + struct obj *obj; + + if (!reference || reference_sz != sizeof(struct obj)) + return NULL; + obj = (struct obj *)reference; + + if (zpc_key_update(obj) == OSSL_RV_ERR) + return NULL; + + return obj_get(obj); +} + +static int kmgmt_has(const void *keydata, int selection) +{ + struct obj *obj = (struct obj *)keydata; + int rv = OSSL_RV_TRUE; + + if (!obj) + return OSSL_RV_FALSE; + + if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) { + if (obj->public_only || !obj->origin_blob.p || !obj->ec_key) + rv = OSSL_RV_FALSE; + } + + if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) { + if (!obj->origin_pubkey.p) + rv = OSSL_RV_FALSE; + } + + return rv; +} + +static int ec_export(void *keydata, int selection, + OSSL_CALLBACK *param_cb, void *cbarg) +{ + return kmgmt_export(keydata, selection, param_cb, cbarg, false); +} + +static int ed_export(void *keydata, int selection, + OSSL_CALLBACK *param_cb, void *cbarg) +{ + return kmgmt_export(keydata, selection, param_cb, cbarg, true); +} + +const OSSL_PARAM *kmgmt_export_types_ex(void *provctx __unused, int selection) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + OSSL_PARAM_END, + }; + int idx = 2; /* none */ + + if (selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) + idx = 1; /* dom-param only */ + if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) + idx = 0; /* dom-param + pubkey */ + + return ¶ms[idx]; +} + +static const char *ec_query_operation_name(int operation_id) +{ + return (operation_id == OSSL_OP_SIGNATURE) ? + PROV_NAME_ECDSA : NULL; +} + +static const char *ed25519_query_operation_name(int operation_id) +{ + return (operation_id == OSSL_OP_SIGNATURE) ? + PROV_NAME_ED25519 : NULL; +} + +static const char *ed448_query_operation_name(int operation_id) +{ + return (operation_id == OSSL_OP_SIGNATURE) ? + PROV_NAME_ED448 : NULL; +} + +static const OSSL_PARAM *ec_gettable_params(void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + /* common */ + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + /* ec-specific */ + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0), + OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ec_get_params(void *keydata, OSSL_PARAM params[]) +{ + struct obj *obj = (struct obj *)keydata; + OSSL_PARAM *p; + + if (!obj) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, + OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT); + if (p) { + if (OSSL_PARAM_set_utf8_string(p, "uncompressed") != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p) { + if (ec_enc_pubkey_param(obj, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY); + if (p) { + if (ec_enc_pubkey_param(obj, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_X); + if (p) { + if (ec_pubkey_coord_param(obj, EC_X, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_Y); + if (p) { + if (ec_pubkey_coord_param(obj, EC_Y, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return kmgmt_get_params(obj, params); +} + +static const OSSL_PARAM *ed_gettable_params(void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + /* common */ + OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL), + OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), + /* ed-specific */ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ed_get_params(void *keydata, OSSL_PARAM params[]) +{ + struct obj *obj = (struct obj *)keydata; + OSSL_PARAM *p; + + if (!obj) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY); + if (p) { + if (!obj->origin_pubkey.p || + OSSL_PARAM_set_octet_string(p, obj->origin_pubkey.p, + obj->origin_pubkey.plen) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MANDATORY_DIGEST); + if (p) { + if (OSSL_PARAM_set_utf8_string(p, "") != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return kmgmt_get_params(obj, params); +} + +static const OSSL_PARAM *kmgmt_settable_params(void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + /* none */ + OSSL_PARAM_END, + }; + return params; +} + +static int kmgmt_set_params(void *keydata __unused, + const OSSL_PARAM params[]) +{ + const char *fmt = NULL; + const OSSL_PARAM *p; + int include_public; + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC); + if (p) { + if (OSSL_PARAM_get_int(p, &include_public) != OSSL_RV_OK || + !include_public) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT); + if (p) { + if (OSSL_PARAM_get_utf8_string_ptr(p, &fmt) != OSSL_RV_OK || + !fmt || + OPENSSL_strcasecmp(fmt, OSSL_PKEY_EC_POINT_CONVERSION_FORMAT_UNCOMPRESSED) != 0) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_DISPATCH kmgmt_ecdsa_functions[] = { + DISPATCH_DEFN(KEYMGMT, NEW, kmgmt_new), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, EXPORT, ec_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), + DISPATCH_DEFN(KEYMGMT, QUERY_OPERATION_NAME, ec_query_operation_name), + DISPATCH_DEFN(KEYMGMT, GETTABLE_PARAMS, ec_gettable_params), + DISPATCH_DEFN(KEYMGMT, GET_PARAMS, ec_get_params), + DISPATCH_DEFN(KEYMGMT, SETTABLE_PARAMS, kmgmt_settable_params), + DISPATCH_DEFN(KEYMGMT, SET_PARAMS, kmgmt_set_params), + DISPATCH_END, +}; + +static const OSSL_DISPATCH kmgmt_ed25519_functions[] = { + DISPATCH_DEFN(KEYMGMT, NEW, kmgmt_new), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, EXPORT, ed_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), + DISPATCH_DEFN(KEYMGMT, QUERY_OPERATION_NAME, ed25519_query_operation_name), + DISPATCH_DEFN(KEYMGMT, GETTABLE_PARAMS, ed_gettable_params), + DISPATCH_DEFN(KEYMGMT, GET_PARAMS, ed_get_params), + DISPATCH_DEFN(KEYMGMT, SETTABLE_PARAMS, kmgmt_settable_params), + DISPATCH_DEFN(KEYMGMT, SET_PARAMS, kmgmt_set_params), + DISPATCH_END, +}; + +static const OSSL_DISPATCH kmgmt_ed448_functions[] = { + DISPATCH_DEFN(KEYMGMT, NEW, kmgmt_new), + DISPATCH_DEFN(KEYMGMT, FREE, kmgmt_free), + DISPATCH_DEFN(KEYMGMT, LOAD, kmgmt_load), + DISPATCH_DEFN(KEYMGMT, HAS, kmgmt_has), + DISPATCH_DEFN(KEYMGMT, EXPORT, ed_export), + DISPATCH_DEFN(KEYMGMT, EXPORT_TYPES_EX, kmgmt_export_types_ex), + DISPATCH_DEFN(KEYMGMT, QUERY_OPERATION_NAME, ed448_query_operation_name), + DISPATCH_DEFN(KEYMGMT, GETTABLE_PARAMS, ed_gettable_params), + DISPATCH_DEFN(KEYMGMT, GET_PARAMS, ed_get_params), + DISPATCH_DEFN(KEYMGMT, SETTABLE_PARAMS, kmgmt_settable_params), + DISPATCH_DEFN(KEYMGMT, SET_PARAMS, kmgmt_set_params), + DISPATCH_END, +}; + +const OSSL_ALGORITHM keymgmt_ops[] = { + ALGORITHM_DEFN(PROV_NAME_EC, PROV_PROP, kmgmt_ecdsa_functions, + PROV_DESC_EC), + ALGORITHM_DEFN(PROV_NAME_ED25519, PROV_PROP, kmgmt_ed25519_functions, + PROV_DESC_ED25519), + ALGORITHM_DEFN(PROV_NAME_ED448, PROV_PROP, kmgmt_ed448_functions, + PROV_DESC_ED448), + ALGORITHM_END, +}; diff --git a/src/keymgmt.h b/src/keymgmt.h new file mode 100644 index 00000000..359ec42f --- /dev/null +++ b/src/keymgmt.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _KEYMGMT_H +#define _KEYMGMT_H + +#include + +extern const OSSL_ALGORITHM keymgmt_ops[]; + +#endif /* _KEYMGMT_H */ diff --git a/src/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 */ diff --git a/src/object.c b/src/object.c new file mode 100644 index 00000000..3cb52ef9 --- /dev/null +++ b/src/object.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#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); + OPENSSL_free(obj->origin_pubkey.p); + OPENSSL_free(obj->apqns); + OPENSSL_free(obj->mkvp); + + OPENSSL_free(obj->id); + OPENSSL_free(obj); +} + +void obj_free(struct obj *obj) +{ + if (!obj) + return; + + if (__atomic_sub_fetch(&obj->refcnt, 1, __ATOMIC_SEQ_CST)) + return; + + _obj_free(obj); +} + +struct obj *obj_get(struct obj *obj) +{ + if (!obj) + return NULL; + + __atomic_fetch_add(&obj->refcnt, 1, __ATOMIC_SEQ_CST); + return obj; +} + +struct obj *obj_new(struct provider_ctx *pctx) +{ + struct obj *obj; + + obj = OPENSSL_zalloc(sizeof(struct obj)); + if (!obj) + return NULL; + + obj->pctx = pctx; + + return obj_get(obj); +} diff --git a/src/object.h b/src/object.h new file mode 100644 index 00000000..03942bf0 --- /dev/null +++ b/src/object.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _OBJECT_H +#define _OBJECT_H + +#include + +#include "provider.h" +#include "zpc/ecc_key.h" + +struct data { + size_t plen; + unsigned char *p; +}; + +struct obj { + /* common */ + unsigned int refcnt; + struct provider_ctx *pctx; + + /* prototype - will be replaced by origin_blob */ + char *id; + + /* zpc keys */ + struct zpc_ec_key *ec_key; + + /* origin path attrs */ + char *origin_type; + char *origin_alg; + struct data origin_blob; + struct data origin_pubkey; + + /* origin qeuery attrs */ + char *apqns; + char *mkvp; + + bool public_only; +}; + +struct obj *obj_new(struct provider_ctx *pctx); +struct obj *obj_get(struct obj *obj); +void obj_free(struct obj *obj); + +#endif /* _OBJECT_H */ 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..9d4cdc36 --- /dev/null +++ b/src/provider.c @@ -0,0 +1,382 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include + +#include + +#include "ossl.h" +#include "provider.h" +#include "store.h" +#include "keymgmt.h" +#include "signature.h" +#include "tls.h" +#include "decoder.h" + +#define C(str) (void *)(str) +static const OSSL_ITEM reason_strings[] = { + { ZPC_ERROR_ARG1NULL, + C("argument 1 NULL") }, + { ZPC_ERROR_ARG2NULL, + C("argument 2 NULL") }, + { ZPC_ERROR_ARG3NULL, + C("argument 3 NULL") }, + { ZPC_ERROR_ARG4NULL, + C("argument 4 NULL") }, + { ZPC_ERROR_ARG5NULL, + C("argument 5 NULL") }, + { ZPC_ERROR_ARG6NULL, + C("argument 6 NULL") }, + { ZPC_ERROR_ARG7NULL, + C("argument 7 NULL") }, + { ZPC_ERROR_ARG8NULL, + C("argument 8 NULL") }, + { ZPC_ERROR_ARG1RANGE, + C("argument 1 out of range") }, + { ZPC_ERROR_ARG2RANGE, + C("argument 2 out of range") }, + { ZPC_ERROR_ARG3RANGE, + C("argument 3 out of range") }, + { ZPC_ERROR_ARG4RANGE, + C("argument 4 out of range") }, + { ZPC_ERROR_ARG5RANGE, + C("argument 5 out of range") }, + { ZPC_ERROR_ARG6RANGE, + C("argument 6 out of range") }, + { ZPC_ERROR_ARG7RANGE, + C("argument 7 out of range") }, + { ZPC_ERROR_ARG8RANGE, + C("argument 8 out of range") }, + { ZPC_ERROR_MALLOC, + C("malloc failed") }, + { ZPC_ERROR_KEYNOTSET, + C("no key is set") }, + { ZPC_ERROR_KEYSIZE, + C("invalid key size") }, + { ZPC_ERROR_IVNOTSET, + C("IV not set") }, + { ZPC_ERROR_IVSIZE, + C("invalid IV size") }, + { ZPC_ERROR_TAGSIZE, + C("invalid tag size") }, + { ZPC_ERROR_TAGMISMATCH, + C("tag mismatch") }, + { ZPC_ERROR_HWCAPS, + C("function not supported") }, + { ZPC_ERROR_SMALLOUTBUF, + C("output buffer too small") }, + { ZPC_ERROR_APQNSNOTSET, + C("APQNs not set") }, + { ZPC_ERROR_KEYTYPE, + C("invalid key type") }, + { ZPC_ERROR_KEYTYPENOTSET, + C("key type not set") }, + { ZPC_ERROR_IOCTLGENSECK2, + C("PKEY_GENSECK2 ioctl failed") }, + { ZPC_ERROR_IOCTLCLR2SECK2, + C("PKEY_CLR2SECK2 ioctl failed") }, + { ZPC_ERROR_IOCTLBLOB2PROTK2, + C("PKEY_BLOB2PROTK2 ioctl failed") }, + { ZPC_ERROR_WKVPMISMATCH, + C("wrapping key verification pattern mismatch") }, + { ZPC_ERROR_DEVPKEY, + C("opening /dev/pkey failed") }, + { ZPC_ERROR_CLEN, + C("ciphertext too long") }, + { ZPC_ERROR_MLEN, + C("message too long") }, + { ZPC_ERROR_AADLEN, + C("additional authenticated data too long") }, + { ZPC_ERROR_PARSE, + C("parse error") }, + { ZPC_ERROR_APQNNOTFOUND, + C("APQN not found in APQN list") }, + { ZPC_ERROR_MKVPLEN, + C("MKVP too long") }, + { ZPC_ERROR_INITLOCK, + C("initializing a lock failed") }, + { ZPC_ERROR_OBJINUSE, + C("object is in use") }, + { ZPC_ERROR_IOCTLAPQNS4KT, + C("PKEY_APQNS4KT ioctl failed") }, + { ZPC_ERROR_KEYSIZENOTSET, + C("key-size not set") }, + { ZPC_ERROR_IOCTLGENPROTK, + C("PKEY_GENPROTK ioctl failed") }, + { ZPC_ERROR_PROTKEYONLY, + C("protected-key only") }, + { ZPC_ERROR_KEYSEQUAL, + C("keys are equal") }, + { ZPC_ERROR_NOTSUP, + C("not supported") }, + { ZPC_ERROR_EC_INVALID_CURVE, + C("Invalid EC curve") }, + { ZPC_ERROR_EC_CURVE_NOTSET, + C("EC curve not set") }, + { ZPC_ERROR_EC_PRIVKEY_NOTSET, + C("EC private key not set") }, + { ZPC_ERROR_EC_PUBKEY_NOTSET, + C("EC public key not set") }, + { ZPC_ERROR_EC_NO_KEY_PARTS, + C("No EC key parts given") }, + { ZPC_ERROR_EC_SIGNATURE_INVALID, + C("signature invalid") }, + { ZPC_ERROR_IOCTLBLOB2PROTK3, + C("PKEY_BLOB2PROTK3 ioctl failed") }, + { ZPC_ERROR_IOCTLCLR2SECK3, + C("PKEY_CLR2SECK3 ioctl failed") }, + { ZPC_ERROR_APQNS_NOTSET, + C("No APQNs set for this key, but required for this operation") }, + { ZPC_ERROR_EC_SIGNATURE_LENGTH, + C("Signature length is invalid for this EC key") }, + { ZPC_ERROR_EC_KEY_PARTS_INCONSISTENT, + C("Given public/private key parts are inconsistent") }, + { ZPC_ERROR_CCA_HOST_LIB_NOT_AVAILABLE, + C("CCA host library not available, but required for this operation") }, + { ZPC_ERROR_EP11_HOST_LIB_NOT_AVAILABLE, + C("EP11 host library not available, but required for this operation") }, + { ZPC_ERROR_EC_PUBKEY_LENGTH, + C("The given EC public key length is invalid") }, + { ZPC_ERROR_EC_PRIVKEY_LENGTH, + C("The given EC private key length is invalid") }, + { ZPC_ERROR_EC_NO_CCA_SECUREKEY_TOKEN, + C("The given buffer does not contain a valid CCA secure key token") }, + { ZPC_ERROR_EC_NO_EP11_SECUREKEY_TOKEN, + C("The given buffer does not contain a valid EP11 secure key token") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_LENGTH, + C("The imported buffer contains an EP11 SPKI with an invalid length") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_CURVE, + C("The imported buffer contains an EP11 SPKI with an invalid EC curve") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_PUBKEY, + C("The imported buffer contains an EP11 SPKI with an invalid public key") }, + { ZPC_ERROR_EC_EP11_SPKI_INVALID_MKVP, + C("The imported buffer contains an EP11 MACed SPKI with an invalid MKVP") }, + { ZPC_ERROR_BLOB_NOT_PKEY_EXTRACTABLE, + C("The imported buffer contains a key blob that cannot be transformed into a protected key.") }, + { ZPC_ERROR_APQNS_INVALID_VERSION, + C("At least one APQN version is invalid for this function.") }, + { ZPC_ERROR_AES_NO_EP11_SECUREKEY_TOKEN, + C("The given buffer does not contain a valid EP11 AES secure key token.") }, + { ZPC_ERROR_AES_NO_CCA_DATAKEY_TOKEN, + C("The given buffer does not contain a valid CCA datakey token") }, + { ZPC_ERROR_AES_NO_CCA_CIPHERKEY_TOKEN, + C("The given buffer does not contain a valid CCA cipherkey token") }, + { ZPC_ERROR_RNDGEN, + C("Error creating random bytes") }, + { ZPC_ERROR_GCM_IV_CREATED_INTERNALLY, + C("Invalid usage of a gcm context with an internally created iv") }, + { ZPC_ERROR_UV_PVSECRETS_NOT_AVAILABLE, + C("Support for UV retrievable secrets is not available, but required for this function.") }, + { ZPC_ERROR_PVSECRET_TYPE_NOT_SUPPORTED, + C("The given pvsecret type is not supported by libzpc.") }, + { ZPC_ERROR_PVSECRET_ID_NOT_FOUND_IN_UV_OR_INVALID_TYPE, + C("The given pvsecret ID does either not exist or belongs to a different secret type.") }, + { ZPC_ERROR_IOCTLVERIFYKEY2, + C("PKEY_VERIFYKEY2 ioctl failed.") }, + { ZPC_ERROR_HMAC_HASH_FUNCTION_NOTSET, + C("HMAC hash function not set.") }, + { ZPC_ERROR_HMAC_HASH_FUNCTION_INVALID, + C("HMAC hash function invalid.") }, + { ZPC_ERROR_HMAC_KEYGEN_VIA_SYSFS, + C("HMAC key generation via sysfs attributes failed.") }, + { ZPC_ERROR_CREATE_BLOCKSIZED_KEY, + C("Creating a block-sized HMAC key failed.") }, + { ZPC_ERROR_XTS_KEYGEN_VIA_SYSFS, + C("Creating a full-xts key via sysfs attributes failed") }, + { 0, NULL }, +}; +#undef C + +static const OSSL_PARAM prov_param_types[] = { + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_VERSION, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_BUILDINFO, OSSL_PARAM_UTF8_PTR, NULL, 0), + OSSL_PARAM_DEFN(OSSL_PROV_PARAM_STATUS, OSSL_PARAM_INTEGER, NULL, 0), + OSSL_PARAM_END, +}; + +static int prov_ctx_init(struct provider_ctx *pctx, const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in) +{ + const OSSL_DISPATCH *iter_in; + OSSL_LIB_CTX *libctx; + + if (!pctx) + return OSSL_RV_ERR; + + libctx = OSSL_LIB_CTX_new_from_dispatch(handle, in); + if (!libctx) + return OSSL_RV_ERR; + + pctx->libctx = libctx; + pctx->handle = handle; + pctx->state = PROVIDER_INITIALIZED; + + for (iter_in = in; iter_in->function_id != 0; iter_in++) { + switch (iter_in->function_id) { + case OSSL_FUNC_CORE_NEW_ERROR: + pctx->core_new_error = OSSL_FUNC_core_new_error(iter_in); + break; + case OSSL_FUNC_CORE_SET_ERROR_DEBUG: + pctx->core_set_error_debug = OSSL_FUNC_core_set_error_debug(iter_in); + break; + case OSSL_FUNC_CORE_VSET_ERROR: + pctx->core_vset_error = OSSL_FUNC_core_vset_error(iter_in); + break; + default: + continue; + } + } + return OSSL_RV_OK; +} + +static void prov_teardown(void *vpctx) +{ + OPENSSL_free(vpctx); +} + +static const OSSL_PARAM *prov_gettable_params(void *vpctx __unused) +{ + return prov_param_types; +} + +static int prov_get_params(void *vpctx, OSSL_PARAM params[]) +{ + struct provider_ctx *pctx = vpctx; + OSSL_PARAM *p; + + if (!pctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_NAME); + if (p && (OSSL_PARAM_set_utf8_ptr(p, PROV_NAME) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_VERSION); + if (p && (OSSL_PARAM_set_utf8_ptr(p, PROV_VERSION) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_BUILDINFO); + if (p && (OSSL_PARAM_set_utf8_ptr(p, PROV_VERSION) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_PROV_PARAM_STATUS); + if (p && (OSSL_PARAM_set_int(p, pctx->state) != OSSL_RV_OK)) + return OSSL_RV_ERR; + + return OSSL_RV_OK; +} + +static const OSSL_ALGORITHM *prov_query_operation(void *vpctx, int operation_id, int *no_cache) +{ + struct provider_ctx *pctx = vpctx; + const OSSL_ALGORITHM *ops; + + if (!pctx || pctx->state == PROVIDER_UNINITIALIZED) + return NULL; + + switch (operation_id) { + case OSSL_OP_STORE: + ops = store_ops; + break; + case OSSL_OP_KEYMGMT: + ops = keymgmt_ops; + break; + case OSSL_OP_SIGNATURE: + ops = signature_ops; + break; + case OSSL_OP_DECODER: + ops = decoder_ops; + break; + default: + ops = NULL; + goto out; + } + + if (no_cache) + *no_cache = OSSL_RV_FALSE; +out: + 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; +} + +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_CAPABILITIES, FUNC(prov_get_capabilities) }, + { OSSL_FUNC_PROVIDER_GET_REASON_STRINGS, FUNC(prov_get_reason_strings) }, + { 0, NULL } +#undef FUNC +}; + +static int prov_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, + void **vpctx) +{ + struct provider_ctx *pctx; + int rv = OSSL_RV_ERR; + + if (!handle || !in || !out || !vpctx) + return OSSL_RV_ERR; + + pctx = OPENSSL_zalloc(sizeof(*pctx)); + if (!pctx) + return OSSL_RV_ERR; + + if (!prov_ctx_init(pctx, handle, in)) + goto err; + + *vpctx = pctx; + *out = provider_dispatch_table; + return OSSL_RV_OK; + +err: + OPENSSL_free(pctx); + return rv; +} + +void prov_err_raise(struct provider_ctx *pctx, const char *file, int line, + const char *func, int reason, const char *fmt, ...) +{ + va_list args; + + if (!pctx || !pctx->core_new_error || + !pctx->core_set_error_debug || !pctx->core_vset_error) + return ERR_raise(ERR_LIB_PROV, reason); + + va_start(args, fmt); + pctx->core_new_error(pctx->handle); + pctx->core_set_error_debug(pctx->handle, file, line, func); + pctx->core_vset_error(pctx->handle, reason, fmt, args); + va_end(args); +} + +int OSSL_provider_init(const OSSL_CORE_HANDLE *handle, + const OSSL_DISPATCH *in, + const OSSL_DISPATCH **out, + void **provctx) +{ + return prov_init(handle, in, out, provctx); +} diff --git a/src/provider.h b/src/provider.h new file mode 100644 index 00000000..2ab36556 --- /dev/null +++ b/src/provider.h @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _PROVIDER_H +#define _PROVIDER_H + +#include +#include +#include + +#define PROV_NAME "hbkzpc" +#define PROV_VERSION "2.0.0" +#define PROV_PROP "provider="PROV_NAME +#define PROV_PROP_FWD "provider!="PROV_NAME + +#define PROV_NAME_EC "EC" +#define PROV_NAMES_EC "EC:id-ecPublicKey:1.2.840.10045.2.1" +#define PROV_DESC_EC "hbkzpc EC implementation" + +#define PROV_NAME_ECDSA "ECDSA" +#define PROV_NAMES_ECDSA PROV_NAME_ECDSA +#define PROV_DESC_ECDSA "hbkzpc ECDSA Implementation" + +#define PROV_NAME_ED25519 "ED25519" +#define PROV_NAMES_ED25519 "ED25519:1.3.101.112" +#define PROV_DESC_ED25519 "hbkzpc ED25519 Implementation" + +#define PROV_NAME_ED448 "ED448" +#define PROV_NAMES_ED448 "ED448:1.3.101.113" +#define PROV_DESC_ED448 "hbkzpc ED448 Implementation" + +#define __unused __attribute__((unused)) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +enum provider_state { + PROVIDER_UNINITIALIZED = 0, + PROVIDER_INITIALIZED, +}; + +struct provider_ctx { + const OSSL_CORE_HANDLE *handle; + OSSL_LIB_CTX *libctx; + + enum provider_state state; + + OSSL_FUNC_core_new_error_fn *core_new_error; + OSSL_FUNC_core_set_error_debug_fn *core_set_error_debug; + OSSL_FUNC_core_vset_error_fn *core_vset_error; +}; + +void prov_err_raise(struct provider_ctx *pctx, const char *file, int line, + const char *func, int reason, const char *fmt, ...); +#define PROV_ERR_raise(pctx, reason) \ + prov_err_raise(pctx, OPENSSL_FILE, OPENSSL_LINE, OPENSSL_FUNC, reason, NULL) + +#endif /* _PROVIDER_H */ diff --git a/src/signature.c b/src/signature.c new file mode 100644 index 00000000..32150d43 --- /dev/null +++ b/src/signature.c @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include +#include +#include + +#include "provider.h" +#include "signature.h" +#include "object.h" +#include "ossl.h" +#include "map.h" +#include "algid.h" +#include "zpc/ecdsa_ctx.h" + +/* REVISIT deterministic nonce support (OSSL_SIGNATURE_PARAM_NONCE_TYPE) */ + +#define ASN1_SIG_HDR 8 + +typedef int (*set_ctx_params_fn)(void *, const OSSL_PARAM *); + +enum sig_op { + SIG_OP_UNDEF = 0, + SIG_OP_SIGN, + SIG_OP_VERIFY, +}; + +enum sig_fmt { + SIG_FMT_UNDEF = 0, + SIG_FMT_RAW, + SIG_FMT_DER, +}; + +static const int ecdsa_curves[] = { + ZPC_EC_CURVE_P256, + ZPC_EC_CURVE_P384, + ZPC_EC_CURVE_P521, +}; + +static const int ed25519_curves[] = { + ZPC_EC_CURVE_ED25519, +}; + +static const int ed448_curves[] = { + ZPC_EC_CURVE_ED448, +}; + +/* supported instance strings */ +static const char *ed25519 = "Ed25519"; +static const char *ed448 = "Ed448"; +static const unsigned char *edctx = { 0 }; + +struct sig_ctx { + struct provider_ctx *pctx; + char *propq; + + EVP_MD_CTX *fwd_md_ctx; + struct zpc_ecdsa_ctx *zpc_ctx; + + enum sig_op op; + enum sig_fmt fmt; + + const int *curves; + size_t curves_len; + + set_ctx_params_fn set_ctx_params; + + const char *ed_instance; + const unsigned char *ed_ctx; + size_t ed_ctxlen; +}; + +static bool valid_curve(int curve, const int *curves, size_t curves_len) +{ + for (size_t i = 0; i < curves_len; i++) + if (curve == curves[i]) + return true; + + return false; +} + +static int raw2der(const unsigned char *raw, size_t rawlen, + unsigned char *der, size_t *derlen) +{ + BIGNUM *r = NULL, *s = NULL; + ECDSA_SIG *ec_sig = NULL; + int rv = OSSL_RV_ERR; + size_t _derlen; + + if (!raw || !rawlen || !derlen || + (*derlen < (rawlen + ASN1_SIG_HDR))) + goto out; + + ec_sig = ECDSA_SIG_new(); + if (!ec_sig) + goto out; + + if (!(r = BN_bin2bn(raw, rawlen / 2, NULL)) || + !(s = BN_bin2bn(raw + rawlen / 2, rawlen / 2, NULL)) || + ECDSA_SIG_set0(ec_sig, r, s) != OSSL_RV_OK) { + BN_clear_free(r); + BN_clear_free(s); + goto out; + } + + _derlen = i2d_ECDSA_SIG(ec_sig, &der); + if (_derlen <= 0) + goto out; + + *derlen = _derlen; + rv = OSSL_RV_OK; +out: + ECDSA_SIG_free(ec_sig); + return rv; +} + +static int der2raw(const unsigned char *der, size_t derlen, + unsigned char *raw, size_t *rawlen) +{ + ECDSA_SIG *ec_sig = NULL; + int rv = OSSL_RV_ERR; + const BIGNUM *r, *s; + size_t _rawlen; + + if (!der || !derlen || !rawlen) + goto out; + _rawlen = *rawlen; + + ec_sig = d2i_ECDSA_SIG(NULL, &der, derlen); + if (!ec_sig) + goto out; + + r = ECDSA_SIG_get0_r(ec_sig); + s = ECDSA_SIG_get0_s(ec_sig); + if (!r || !s) + goto out; + + if ((BN_bn2binpad(r, raw, _rawlen / 2) == -1) || + (BN_bn2binpad(s, raw + _rawlen / 2, _rawlen / 2) == -1)) + goto out; + + rv = OSSL_RV_OK; +out: + ECDSA_SIG_free(ec_sig); + return rv; +} + +static struct sig_ctx *sig_newctx(struct provider_ctx *pctx, const char *propq, + const int *curves, size_t curves_len, + enum sig_fmt fmt, set_ctx_params_fn set_ctx_params, + const char *ed_instance, + const unsigned char *ed_ctx, size_t ed_ctxlen) +{ + struct zpc_ecdsa_ctx *zpc_ctx = NULL; + struct sig_ctx *sctx = NULL; + char *pq; + int rc; + + if (!pctx || fmt == SIG_FMT_UNDEF) + goto err; + + rc = zpc_ecdsa_ctx_alloc(&zpc_ctx); + if (rc) { + PROV_ERR_raise(pctx, rc); + goto err; + } + + sctx = OPENSSL_zalloc(sizeof(struct sig_ctx)); + if (!sctx) + goto err; + + pq = OPENSSL_strdup(propq); + if (pq) + goto err; + + sctx->pctx = pctx; + sctx->zpc_ctx = zpc_ctx; + sctx->propq = pq; + sctx->curves = curves; + sctx->curves_len = curves_len; + sctx->fmt = fmt; + sctx->set_ctx_params = set_ctx_params; + sctx->ed_instance = ed_instance; + sctx->ed_ctx = ed_ctx; + sctx->ed_ctxlen = ed_ctxlen; + + return sctx; +err: + OPENSSL_free(sctx); + zpc_ecdsa_ctx_free(&zpc_ctx); + return NULL; +} + +static int sig_sctx_set_md(struct sig_ctx *sctx, const char *mdname, + const OSSL_PARAM params[]) +{ + EVP_MD_CTX *md_ctx = NULL; + const EVP_MD *md; + + if (!sctx) + return OSSL_RV_ERR; + + if (mdname) { + if (!(md = EVP_MD_fetch(sctx->pctx->libctx, mdname, PROV_PROP_FWD)) || + !(md_ctx = EVP_MD_CTX_new()) || + (EVP_DigestInit_ex2(md_ctx, md, params) != OSSL_RV_OK)) + return OSSL_RV_ERR; + } + + EVP_MD_CTX_free(sctx->fwd_md_ctx); + sctx->fwd_md_ctx = md_ctx; + + return OSSL_RV_OK; +} + +static int sig_init(struct sig_ctx *sctx, struct obj *obj, + const OSSL_PARAM params[], + enum sig_op op) +{ + int rc; + + if (!sctx) + return OSSL_RV_ERR; + + if (obj) { + if (!valid_curve(obj_key_curve(obj), + sctx->curves, sctx->curves_len)) + return OSSL_RV_ERR; + + if ((rc = zpc_ecdsa_ctx_set_key(sctx->zpc_ctx, obj->ec_key))) { + PROV_ERR_raise(sctx->pctx, rc); + return OSSL_RV_ERR; + } + } + + sctx->op = op; + + return sctx->set_ctx_params ? + sctx->set_ctx_params(sctx, params) : + OSSL_RV_OK; +} + +static int sig_digest_init(struct sig_ctx *sctx, const char *mdname, + struct obj *obj, const OSSL_PARAM params[], + enum sig_op op) +{ + int rv; + + if (!sctx) + return OSSL_RV_ERR; + + rv = sctx->fwd_md_ctx ? + EVP_MD_CTX_reset(sctx->fwd_md_ctx) : + sig_sctx_set_md(sctx, mdname, params); + if (rv != OSSL_RV_OK) + return rv; + + return sig_init(sctx, obj, params, op); +} + +static int sig_sign_raw(struct sig_ctx *sctx, + unsigned char *sig, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + *siglen = sigsize; + return zpc_ecdsa_sign(sctx->zpc_ctx, tbs, tbslen, sig, siglen) + ? OSSL_RV_ERR + : OSSL_RV_OK; +} + +static int sig_sign_der(struct sig_ctx *sctx, + unsigned char *sig, size_t *siglen, size_t sigsize, + const unsigned char *tbs, size_t tbslen) +{ + unsigned char raw[EVP_MAX_MD_SIZE]; + unsigned char *_sig; + size_t rawlen; + int rv; + + rawlen = MIN(sigsize, EVP_MAX_MD_SIZE); + _sig = sig ? raw : NULL; + + rv = sig_sign_raw(sctx, _sig, &rawlen, rawlen, tbs, tbslen); + if (rv != OSSL_RV_OK) + return rv; + + if (!sig) + *siglen = rawlen + ASN1_SIG_HDR; + else + rv = raw2der(raw, rawlen, sig, siglen); + + return rv; +} + +static int sig_verify_raw(struct sig_ctx *sctx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + if (!sctx || + !sctx->zpc_ctx || + sctx->op != SIG_OP_VERIFY) + return OSSL_RV_ERR; + + return zpc_ecdsa_verify(sctx->zpc_ctx, tbs, tbslen, sig, siglen) + ? OSSL_RV_ERR + : OSSL_RV_OK; +} + +static int sig_verify_der(struct sig_ctx *sctx, + const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + unsigned char raw[EVP_MAX_MD_SIZE]; + size_t rawlen; + int rv; + + /* calculate raw signature size */ + if (zpc_ecdsa_sign(sctx->zpc_ctx, tbs, tbslen, NULL, &rawlen)) + return OSSL_RV_ERR; + + rv = der2raw(sig, siglen, raw, &rawlen); + if (rv != OSSL_RV_OK) + return rv; + + return sig_verify_raw(sctx, raw, rawlen, tbs, tbslen); +} + +#define DISP_SIG(tname, name) DECL_DISPATCH_FUNC(signature, tname, name) +DISP_SIG(newctx, ecdsa_newctx); +#if 0 +DISP_SIG(newctx, ed25519_newctx); +DISP_SIG(newctx, ed448_newctx); +#endif +DISP_SIG(freectx, sig_freectx); + +DISP_SIG(sign_init, sig_sign_init); +DISP_SIG(sign, sig_sign); +DISP_SIG(verify_init, sig_verify_init); +DISP_SIG(verify, sig_verify); + +DISP_SIG(digest_sign_init, sig_digest_sign_init); +DISP_SIG(digest_sign_update, sig_digest_update); +DISP_SIG(digest_sign_final, sig_digest_sign_final); +DISP_SIG(digest_verify_init, sig_digest_verify_init); +DISP_SIG(digest_verify_update, sig_digest_update); +DISP_SIG(digest_verify_final, sig_digest_verify_final); + +DISP_SIG(gettable_ctx_params, ecdsa_gettable_ctx_params); +DISP_SIG(get_ctx_params, ecdsa_get_ctx_params); +DISP_SIG(settable_ctx_params, ecdsa_settable_ctx_params); +DISP_SIG(set_ctx_params, ecdsa_set_ctx_params); + +DISP_SIG(gettable_ctx_params, eddsa_gettable_ctx_params); +DISP_SIG(get_ctx_params, eddsa_get_ctx_params); +DISP_SIG(settable_ctx_params, eddsa_settable_ctx_params); +DISP_SIG(set_ctx_params, eddsa_set_ctx_params); + +#undef DISP_SIG + +/* dispatch */ +static void *ecdsa_newctx(void *vpctx, const char *propq) +{ + return sig_newctx(vpctx, propq, ecdsa_curves, ARRAY_SIZE(ecdsa_curves), + SIG_FMT_DER, ecdsa_set_ctx_params, NULL, NULL, 0); +} + +static void *ed25519_newctx(void *vpctx, const char *propq) +{ + return sig_newctx(vpctx, propq, ed25519_curves, ARRAY_SIZE(ed25519_curves), + SIG_FMT_RAW, eddsa_set_ctx_params, ed25519, edctx, 0); +} + +static void *ed448_newctx(void *vpctx, const char *propq) +{ + return sig_newctx(vpctx, propq, ed448_curves, ARRAY_SIZE(ed448_curves), + SIG_FMT_RAW, eddsa_set_ctx_params, ed448, edctx, 0); +} + +static void sig_freectx(void *vsctx) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + + if (!sctx) + return; + + zpc_ecdsa_ctx_free(&sctx->zpc_ctx); + EVP_MD_CTX_free(sctx->fwd_md_ctx); + OPENSSL_free(sctx->propq); + OPENSSL_free(sctx); +} + +/* dispatch prehashed sign/verify */ +static int sig_sign_init(void *vsctx, void *provkey, + const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_SIGN); +} + +static int sig_verify_init(void *vsctx, void *provkey, + const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_VERIFY); +} + +static int sig_sign(void *vsctx, unsigned char *sig, size_t *siglen, + size_t sigsize, const unsigned char *tbs, size_t tbslen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + size_t _siglen; + int rv; + + if (!sctx || !tbs || + !sctx->zpc_ctx || + sctx->op != SIG_OP_SIGN) + return OSSL_RV_ERR; + + _siglen = siglen ? *siglen : sigsize; + + rv = (sctx->fmt == SIG_FMT_RAW) ? + sig_sign_raw(sctx, sig, &_siglen, sigsize, tbs, tbslen) : + sig_sign_der(sctx, sig, &_siglen, sigsize, tbs, tbslen); + + if (rv == OSSL_RV_OK) + if (siglen) + *siglen = _siglen; + + return rv; +} + +static int sig_verify(void *vsctx, const unsigned char *sig, size_t siglen, + const unsigned char *tbs, size_t tbslen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + + if (!sctx || !sig || !tbs || + !sctx->zpc_ctx || + sctx->op != SIG_OP_VERIFY) + return OSSL_RV_ERR; + + return (sctx->fmt == SIG_FMT_RAW) ? + sig_verify_raw(sctx, sig, siglen, tbs, tbslen) : + sig_verify_der(sctx, sig, siglen, tbs, tbslen); +} + +/* dispatch digest sign/verify */ +static int sig_digest_sign_init(void *vsctx, const char *mdname, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_digest_init(vsctx, mdname, provkey, params, SIG_OP_SIGN); +} + +static int sig_digest_verify_init(void *vsctx, const char *mdname, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_digest_init(vsctx, mdname, provkey, params, SIG_OP_VERIFY); +} + +static int sig_digest_update(void *vsctx, const unsigned char *data, + size_t datalen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + + if (!sctx) + return OSSL_RV_ERR; + + return EVP_DigestUpdate(sctx->fwd_md_ctx, data, datalen); +} + +static int sig_digest_sign_final(void *vsctx, unsigned char *sig, + size_t *siglen, size_t sigsize) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + unsigned char tbs[EVP_MAX_MD_SIZE]; + unsigned int tbslen = EVP_MAX_MD_SIZE; + + if (!sctx) + return OSSL_RV_ERR; + + if (sig && + EVP_DigestFinal_ex(sctx->fwd_md_ctx, tbs, &tbslen) != OSSL_RV_OK) + return OSSL_RV_ERR; + + return sig_sign(sctx, sig, siglen, sigsize, tbs, tbslen); +} + +static int sig_digest_verify_final(void *vsctx, const unsigned char *sig, + size_t siglen) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + unsigned char tbs[EVP_MAX_MD_SIZE]; + unsigned int tbslen = EVP_MAX_MD_SIZE; + + if (!sctx) + return OSSL_RV_ERR; + + if (EVP_DigestFinal_ex(sctx->fwd_md_ctx, tbs, &tbslen) != OSSL_RV_OK) + return OSSL_RV_ERR; + + return sig_verify(sctx, sig, siglen, tbs, tbslen); +} + +static int ed_digest_sign_init(void *vsctx, const char *mdname __unused, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_SIGN); +} + +static int ed_digest_verify_init(void *vsctx, const char *mdname __unused, + void *provkey, const OSSL_PARAM params[]) +{ + return sig_init(vsctx, provkey, params, SIG_OP_VERIFY); +} + +/* dispatch get/set */ +static const OSSL_PARAM *ecdsa_gettable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ecdsa_get_ctx_params(void *vsctx, OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p) { + int type; + + if (!sctx->fwd_md_ctx) + return OSSL_RV_ERR; + + type = EVP_MD_CTX_get_type(sctx->fwd_md_ctx); + if (algid_ecdsa(type, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p) { + /* REVISIT deterministic nonce support */ + if (OSSL_PARAM_set_uint(p, 0) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE); + if (p) { + int dsz = EVP_MD_CTX_get_size(sctx->fwd_md_ctx); + + if (dsz <= 0 || (OSSL_PARAM_set_size_t(p, dsz) != OSSL_RV_OK)) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p) { + const char *d = EVP_MD_CTX_get0_name(sctx->fwd_md_ctx); + if (!d || + OSSL_PARAM_set_utf8_string(p, d) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *ecdsa_settable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0), + OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int ecdsa_set_ctx_params(void *vsctx, const OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + const OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE); + if (p) { + unsigned int nonce_type; + + /* REVISIT deterministic nonce support */ + if ((OSSL_PARAM_get_uint(p, &nonce_type) != OSSL_RV_OK) || + (nonce_type != 0)) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST); + if (p) { + const char *mdname = NULL; + + if (OSSL_PARAM_get_utf8_string_ptr(p, &mdname) != OSSL_RV_OK || + sig_sctx_set_md(sctx, mdname, params) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *eddsa_gettable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0), + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int eddsa_get_ctx_params(void *vsctx, OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID); + if (p) { + if (algid_eddsa(sctx->ed_instance, p) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_INSTANCE); + if (p) { + if (OSSL_PARAM_set_utf8_string(p, sctx->ed_instance) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p) { + if (OSSL_PARAM_set_octet_string(p, sctx->ed_ctx, sctx->ed_ctxlen) != OSSL_RV_OK) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *eddsa_settable_ctx_params(void *vsctx __unused, + void *vpctx __unused) +{ + static const OSSL_PARAM params[] = { + OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0), + OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0), + OSSL_PARAM_END, + }; + return params; +} + +static int eddsa_set_ctx_params(void *vsctx, const OSSL_PARAM params[]) +{ + struct sig_ctx *sctx = (struct sig_ctx *)vsctx; + const OSSL_PARAM *p; + + if (!sctx) + return OSSL_RV_ERR; + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_INSTANCE); + if (p) { + const char *edi = NULL; + + if (OSSL_PARAM_get_utf8_string_ptr(p, &edi) != OSSL_RV_OK || + OPENSSL_strcasecmp(sctx->ed_instance, edi) != 0) + return OSSL_RV_ERR; + } + + p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING); + if (p) { + const void *edc = NULL; + size_t edcl; + + if (OSSL_PARAM_get_octet_string_ptr(p, &edc, &edcl) != OSSL_RV_OK || + sctx->ed_ctxlen != edcl || + memcmp(sctx->ed_ctx, edc, edcl) != 0) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_DISPATCH ecdsa_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ecdsa_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, VERIFY_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_INIT, sig_digest_sign_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_UPDATE, sig_digest_update), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_FINAL, sig_digest_sign_final), + + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_INIT, sig_digest_verify_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_UPDATE, sig_digest_update), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_FINAL, sig_digest_verify_final), + + DISPATCH_DEFN(SIGNATURE, GETTABLE_CTX_PARAMS, ecdsa_gettable_ctx_params), + DISPATCH_DEFN(SIGNATURE, GET_CTX_PARAMS, ecdsa_get_ctx_params), + DISPATCH_DEFN(SIGNATURE, SETTABLE_CTX_PARAMS, ecdsa_settable_ctx_params), + DISPATCH_DEFN(SIGNATURE, SET_CTX_PARAMS, ecdsa_set_ctx_params), + + DISPATCH_END, +}; + +static const OSSL_DISPATCH ed25519_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ed25519_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_MESSAGE_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, VERIFY_MESSAGE_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_INIT, ed_digest_sign_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_INIT, ed_digest_verify_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, GETTABLE_CTX_PARAMS, eddsa_gettable_ctx_params), + DISPATCH_DEFN(SIGNATURE, GET_CTX_PARAMS, eddsa_get_ctx_params), + DISPATCH_DEFN(SIGNATURE, SETTABLE_CTX_PARAMS, eddsa_settable_ctx_params), + DISPATCH_DEFN(SIGNATURE, SET_CTX_PARAMS, eddsa_set_ctx_params), + + DISPATCH_END, +}; + +static const OSSL_DISPATCH ed448_functions[] = { + DISPATCH_DEFN(SIGNATURE, NEWCTX, ed448_newctx), + DISPATCH_DEFN(SIGNATURE, FREECTX, sig_freectx), + + DISPATCH_DEFN(SIGNATURE, SIGN_MESSAGE_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN_INIT, sig_sign_init), + DISPATCH_DEFN(SIGNATURE, SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, VERIFY_MESSAGE_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY_INIT, sig_verify_init), + DISPATCH_DEFN(SIGNATURE, VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN_INIT, ed_digest_sign_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_SIGN, sig_sign), + + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY_INIT, ed_digest_verify_init), + DISPATCH_DEFN(SIGNATURE, DIGEST_VERIFY, sig_verify), + + DISPATCH_DEFN(SIGNATURE, GETTABLE_CTX_PARAMS, eddsa_gettable_ctx_params), + DISPATCH_DEFN(SIGNATURE, GET_CTX_PARAMS, eddsa_get_ctx_params), + DISPATCH_DEFN(SIGNATURE, SETTABLE_CTX_PARAMS, eddsa_settable_ctx_params), + DISPATCH_DEFN(SIGNATURE, SET_CTX_PARAMS, eddsa_set_ctx_params), + + DISPATCH_END, +}; + +const OSSL_ALGORITHM signature_ops[] = { + ALGORITHM_DEFN(PROV_NAMES_ECDSA, PROV_PROP, ecdsa_functions, PROV_DESC_ECDSA), + ALGORITHM_DEFN(PROV_NAMES_ED25519, PROV_PROP, ed25519_functions, PROV_DESC_ED25519), + ALGORITHM_DEFN(PROV_NAMES_ED448, PROV_PROP, ed448_functions, PROV_DESC_ED448), + ALGORITHM_END, +}; diff --git a/src/signature.h b/src/signature.h new file mode 100644 index 00000000..fbeed32f --- /dev/null +++ b/src/signature.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _SIGNATURE_H +#define _SIGNATURE_H + +#include + +extern const OSSL_ALGORITHM signature_ops[]; + +#endif /* _SIGNATURE_H */ diff --git a/src/store.c b/src/store.c new file mode 100644 index 00000000..548b28d8 --- /dev/null +++ b/src/store.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include + +#include "provider.h" +#include "object.h" +#include "ossl.h" +#include "uri.h" +#include "map.h" +#include "store_local.h" + +struct store_ctx { + struct provider_ctx *pctx; + struct parsed_uri *puri; + + int type_exp; + bool eof; +}; + +#define DISP_STORE_FN(tname, name) DECL_DISPATCH_FUNC(store, tname, name) +DISP_STORE_FN(open, store_open); +#ifdef OSSL_FUNC_STORE_OPEN_EX +DISP_STORE_FN(open_ex, store_open_ex); +#endif +DISP_STORE_FN(load, store_load); +DISP_STORE_FN(eof, store_eof); +DISP_STORE_FN(close, store_close); +DISP_STORE_FN(set_ctx_params, store_set_ctx_params); +DISP_STORE_FN(settable_ctx_params, store_settable_ctx_params); +#undef DISP_STORE_FN + +static struct store_ctx *store_ctx_init(struct provider_ctx *pctx) +{ + struct store_ctx *sctx; + + sctx = OPENSSL_zalloc(sizeof(struct store_ctx)); + if (!sctx) + return NULL; + + sctx->pctx = pctx; + sctx->eof = false; + + return sctx; +} + +static void store_ctx_free(struct store_ctx *sctx) +{ + if (!sctx) + return; + + parsed_uri_free(sctx->puri); + OPENSSL_free(sctx); + + return; +} + +static int store_ctx_expect(struct store_ctx *sctx, int type_exp) +{ + int rv = OSSL_RV_OK; + + switch (type_exp) { + case OSSL_STORE_INFO_PUBKEY: + case OSSL_STORE_INFO_PKEY: + sctx->type_exp = type_exp; + break; + default: + rv = OSSL_RV_ERR; + break; + } + + return rv; +} + +static void *store_open(void *vpctx, const char *uri) +{ + struct store_ctx *sctx; + + sctx = store_ctx_init(vpctx); + if (!sctx) + return NULL; + + sctx->puri = parsed_uri_new(uri); + if (!sctx->puri) + goto err; + + return sctx; +err: + store_ctx_free(sctx); + return NULL; +} + +static void *store_open_ex(void *vpctx, const char *uri, + const OSSL_PARAM params[], + OSSL_PASSPHRASE_CALLBACK *pw_cb __unused, + void *pw_cbarg __unused) +{ + struct store_ctx *sctx; + + sctx = store_open(vpctx, uri); + if (!sctx) + return NULL; + + if (store_set_ctx_params(sctx, params) != OSSL_RV_OK) + goto err; + + return sctx; +err: + store_ctx_free(sctx); + return NULL; + +} + +static int attr2str(const struct attr *attr, char **str) +{ + if (!attr->value) + return OSSL_RV_OK; + + if (*str) + return OSSL_RV_ERR; + + *str = OPENSSL_strdup(attr->value); + return (*str) ? OSSL_RV_OK : OSSL_RV_ERR; +} + +static int attr2data(const struct attr *attr, struct data *data) +{ + size_t plen; + + if (!attr || !data) + return OSSL_RV_ERR; + + /* data already set */ + if (data->p || data->plen) + return OSSL_RV_ERR; + + if (!attr->value) + return OSSL_RV_OK; + plen = strlen(attr->value); + + if (OPENSSL_hexstr2buf_ex(NULL, 0, &plen, + attr->value, '\0') != 1) + return OSSL_RV_ERR; + + data->p = OPENSSL_zalloc(plen); + if (!data->p) + return OSSL_RV_ERR; + data->plen = plen; + + if (OPENSSL_hexstr2buf_ex(data->p, data->plen, &data->plen, + attr->value, '\0') != 1) { + OPENSSL_free(data->p); + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static struct obj *uri2obj(struct provider_ctx *pctx, const struct parsed_uri *puri) +{ + struct obj *obj; + + obj = obj_new(pctx); + if (!obj) + return NULL; + + if (attr2str(&puri->origin_type, &obj->origin_type) != OSSL_RV_OK) + goto err; + if (attr2str(&puri->origin_alg, &obj->origin_alg) != OSSL_RV_OK) + goto err; + if (attr2data(&puri->origin_blob, &obj->origin_blob) != OSSL_RV_OK) + goto err; + if (attr2data(&puri->origin_pubkey, &obj->origin_pubkey) != OSSL_RV_OK) + goto err; + if (attr2str(&puri->mkvp, &obj->mkvp) != OSSL_RV_OK) + goto err; + if (attr2str(&puri->apqns, &obj->apqns) != OSSL_RV_OK) + goto err; + + return obj; +err: + obj_free(obj); + return NULL; +} + +static int store_load(void *vsctx, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) +{ + struct store_ctx *sctx = (struct store_ctx *)vsctx; + bool public_only = false; + struct parsed_uri *puri; + int object_type; + + if (!sctx) + return OSSL_RV_ERR; + puri = sctx->puri; + sctx->eof = true; + + object_type = alg2object_type(puri->origin_alg.value); + if (object_type == OSSL_OBJECT_UNKNOWN) + return OSSL_RV_ERR; + + /* early checks */ + switch (sctx->type_exp) { + case OSSL_STORE_INFO_PKEY: + if (object_type != OSSL_OBJECT_PKEY) + return OSSL_RV_ERR; + break; + case OSSL_STORE_INFO_PUBKEY: + if (object_type != OSSL_OBJECT_PKEY) + return OSSL_RV_ERR; + public_only = true; + break; + case 0: + /* no expected type */ + break; + default: + return OSSL_RV_ERR; + } + + return store_load_uri(sctx->pctx, puri, public_only, + object_cb, object_cbarg, pw_cb, pw_cbarg); +} + +static int store_eof(void *vsctx) +{ + struct store_ctx *sctx = (struct store_ctx *)vsctx; + + if (!sctx) + return OSSL_RV_TRUE; + + return sctx->eof ? OSSL_RV_TRUE : OSSL_RV_FALSE; +} + +static int store_close(void *vsctx) +{ + store_ctx_free((struct store_ctx *)vsctx); + return OSSL_RV_OK; +} + +static int store_set_ctx_params(void *vsctx, const OSSL_PARAM params[]) +{ + struct store_ctx *sctx = (struct store_ctx *)vsctx; + const OSSL_PARAM *p; + int type_exp; + + if (!sctx) + return OSSL_RV_ERR; + + if (!params) + return OSSL_RV_OK; + + p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_EXPECT); + if (p) { + if ((OSSL_PARAM_get_int(p, &type_exp) != OSSL_RV_OK) || + (store_ctx_expect(sctx, type_exp) != OSSL_RV_OK)) + return OSSL_RV_ERR; + } + + return OSSL_RV_OK; +} + +static const OSSL_PARAM *store_settable_ctx_params(void *pctx __unused) +{ + static const OSSL_PARAM known_settable_ctx_params[] = { + OSSL_PARAM_int(OSSL_STORE_PARAM_EXPECT, NULL), + OSSL_PARAM_END, + }; + return known_settable_ctx_params; +} + +int store_load_uri(struct provider_ctx *pctx, struct parsed_uri *puri, + bool public_only, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb __unused, + void *pw_cbarg __unused) +{ + OSSL_PARAM params[4]; + struct obj *obj; + char *data_type; + int object_type; + int rv; + + if (!pctx || !puri) + return OSSL_RV_ERR; + + object_type = alg2object_type(puri->origin_alg.value); + if (object_type == OSSL_OBJECT_UNKNOWN) + return OSSL_RV_ERR; + + data_type = alg2data_type(puri->origin_alg.value); + if (!data_type) + return OSSL_RV_ERR; + + obj = uri2obj(pctx, puri); + if (!obj) + return OSSL_RV_ERR; + obj->public_only = public_only; + + params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, + &object_type); + params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, + data_type, 0); + params[2] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE, + obj, sizeof(struct obj)); + params[3] = OSSL_PARAM_construct_end(); + + rv = object_cb(params, object_cbarg); + obj_free(obj); + return rv; +} + +static const OSSL_DISPATCH store_functions[] = { + DISPATCH_DEFN(STORE, OPEN, store_open), +#ifdef OSSL_FUNC_STORE_OPEN_EX + DISPATCH_DEFN(STORE, OPEN_EX, store_open_ex), +#endif + DISPATCH_DEFN(STORE, LOAD, store_load), + DISPATCH_DEFN(STORE, EOF, store_eof), + DISPATCH_DEFN(STORE, CLOSE, store_close), + DISPATCH_DEFN(STORE, SET_CTX_PARAMS, store_set_ctx_params), + DISPATCH_DEFN(STORE, SETTABLE_CTX_PARAMS, store_settable_ctx_params), + DISPATCH_END +}; + +const OSSL_ALGORITHM store_ops[] = { + { URI_PROTOCOL_PREFIX, "provider=" PROV_NAME, store_functions, "HBKZPC URI Store" }, + { NULL, NULL, NULL, NULL }, +}; diff --git a/src/store.h b/src/store.h new file mode 100644 index 00000000..a03c60f9 --- /dev/null +++ b/src/store.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _STORE_H +#define _STORE_H + +#include + +extern const OSSL_ALGORITHM store_ops[]; + +#endif /* _STORE_H */ diff --git a/src/store_local.h b/src/store_local.h new file mode 100644 index 00000000..69715033 --- /dev/null +++ b/src/store_local.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _STORE_LOCAL_H +#define _STORE_LOCAL_H + +#include +#include + +#include "provider.h" +#include "uri.h" + +int store_load_uri(struct provider_ctx *pctx, struct parsed_uri *puri, + bool public_only, + OSSL_CALLBACK *object_cb, void *object_cbarg, + OSSL_PASSPHRASE_CALLBACK *pw_cb , void *pw_cbarg); + +#endif /* _STORE_LOCAL_H */ 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 */ diff --git a/src/uri.c b/src/uri.c new file mode 100644 index 00000000..9402e6b6 --- /dev/null +++ b/src/uri.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include + +#include "uri.h" + +#define SEP_PROTOCOL ":" +#define SEP_PATHQUERY "?" +#define SEP_KEYVALUE "=" +#define SEP_PATHATTRS ";" +#define SEP_QUERYATTRS "&" + +#define URI_PROTOCOL URI_PROTOCOL_PREFIX SEP_PROTOCOL + +#define URI_P_ORIGIN_TYPE "origin-type" SEP_KEYVALUE +#define URI_P_ORIGIN_ALG "origin-alg" SEP_KEYVALUE +#define URI_P_ORIGIN_BLOB "origin-blob" SEP_KEYVALUE +#define URI_P_ORIGIN_PUBKEY "origin-pubkey" SEP_KEYVALUE +#define URI_P_COMMENT "comment" SEP_KEYVALUE + +#define URI_Q_MKVP "mkvp" SEP_KEYVALUE +#define URI_Q_APQNS "apqns" SEP_KEYVALUE + +static void decode_pct(char *s) +{ + char *rp, *wp, *endptr; + unsigned long tmp; + char hex[3] = {0}; + size_t wp_len = 0; + + if (!s) + return; + + if (!strchr(s, '%')) + return; + + rp = wp = s; + while(*rp) { + switch(*rp) { + case '%': + if (strlen(rp) < 3) + goto out; /* invalid format */ + + rp++; /* skip % */ + memcpy(hex, rp, 2); /* 2 chars only */ + + tmp = strtoul(hex, &endptr, 16);/* convert */ + if (*endptr != '\0') + goto out; /* non-hex chars */ + *wp = (char)(tmp & 0xff); + + rp++; + break; + default: + *wp = *rp; + } + + rp++; + wp++; + wp_len++; + } +out: + *wp = '\0'; +} + +static int parse_attr(char *str, struct attr *attr) +{ + char *key, **p; + int rc = 1; + + if (!str || !attr) + goto out; + + /* skip already parsed attribute */ + if (attr->value) + goto out; + + p = &str; + key = strsep(p, SEP_KEYVALUE); + if (!p) + goto out; + + decode_pct(*p); + attr->key = key; + attr->value = *p; + + rc = 0; +out: + return rc; +} + +static inline int match_elem_attrkey(const char *elem, const char *attrkey) +{ + return (strncmp(elem, attrkey, strlen(attrkey)) == 0); +} + +static int parse_query(char *qattr, struct parsed_uri *puri) +{ + char **next; + + /* query attributes are optional */ + if (!qattr || !strlen(qattr)) + return 0; + + next = &qattr; + do { + char *e = strsep(next, SEP_QUERYATTRS); + int rc = 0; + + if (match_elem_attrkey(e, URI_Q_MKVP)) + rc = parse_attr(e, &puri->mkvp); + else if (match_elem_attrkey(e, URI_Q_APQNS)) + rc = parse_attr(e, &puri->apqns); + else + rc = 1; /* unknown attribute */ + if (rc) + return rc; + } while (*next); + + return 0; +} + +static int parse_path(char *pattr, struct parsed_uri *puri) +{ + char **next; + + /* path attributes are mandatory */ + if (!pattr || !strlen(pattr)) + return 1; + + next = &pattr; + do { + char *e = strsep(next, SEP_PATHATTRS); + int rc = 0; + + if (match_elem_attrkey(e, URI_P_ORIGIN_TYPE)) + rc = parse_attr(e, &puri->origin_type); + else if (match_elem_attrkey(e, URI_P_ORIGIN_ALG)) + rc = parse_attr(e, &puri->origin_alg); + else if (match_elem_attrkey(e, URI_P_ORIGIN_BLOB)) + rc = parse_attr(e, &puri->origin_blob); + else if (match_elem_attrkey(e, URI_P_ORIGIN_PUBKEY)) + rc = parse_attr(e, &puri->origin_pubkey); + else if (match_elem_attrkey(e, URI_P_COMMENT)) + rc = parse_attr(e, &puri->comment); + else + rc = 1; /* unknown attribute */ + if (rc) + return rc; + } while (*next); + + return 0; +} + +static int parse(char *uri, struct parsed_uri *puri) +{ + char *pattr, *qattr; + char **next; + int rc; + + if (!uri || !puri) + return 1; + + if (strncmp(uri, URI_PROTOCOL, strlen(URI_PROTOCOL)) != 0) + return 1; + + next = &uri; + + /* drop protocol */ + strsep(next, SEP_PROTOCOL); + + pattr = strsep(next, SEP_PATHQUERY); + qattr = *next; + + rc = parse_path(pattr, puri); + if (rc) { + return rc; + } + + rc = parse_query(qattr, puri); + if (rc) { + return rc; + } + + return 0; +} + +void parsed_uri_free(struct parsed_uri *puri) +{ + if (!puri) + return; + + if (puri->priv) + OPENSSL_clear_free(puri->priv, + puri->privlen); + + OPENSSL_free(puri); +} + +struct parsed_uri *parsed_uri_new(const char *uri) +{ + struct parsed_uri *puri; + + puri = OPENSSL_zalloc(sizeof(struct parsed_uri)); + if (!puri) + return NULL; + + puri->priv = OPENSSL_strdup(uri); + if (!puri->priv) + goto err; + puri->privlen = strlen(uri); + + if(parse(puri->priv, puri)) + goto err; + + return puri; + +err: + parsed_uri_free(puri); + return NULL; +} diff --git a/src/uri.h b/src/uri.h new file mode 100644 index 00000000..79cb7c14 --- /dev/null +++ b/src/uri.h @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#ifndef _URI_H +#define _URI_H + +#include + +#define URI_PROTOCOL_PREFIX "hbkzpc" + +struct attr { + const char *key; + const char *value; + //unsigned char *data; + //size_t datalen; +}; + +struct parsed_uri { + char *priv; + size_t privlen; + + /* path attributes */ + struct attr origin_type; + struct attr origin_alg; + struct attr origin_blob; + struct attr origin_pubkey; + struct attr comment; + + /* query attributes */ + struct attr mkvp; + struct attr apqns; +}; + +struct parsed_uri *parsed_uri_new(const char *uri); +void parsed_uri_free(struct parsed_uri *puri); + +#endif /* _URI_H */ diff --git a/test/t_provider.c b/test/t_provider.c new file mode 100644 index 00000000..a2d2e015 --- /dev/null +++ b/test/t_provider.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: MIT +// Copyright contributors to the libzpc project +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ossl.h" +#include "asn1.h" + +#define PROVIDER "hbkzpc" +#define PROP_PROVIDER "provider=" PROVIDER + +#define HBKZPC_URI_ECDSA_UV "hbkzpc:comment=uv-secret-name=ecdsa" \ + ";origin-alg=prime256v1" \ + ";origin-type=uv" \ + ";origin-blob=318f9f812b9e89ec3bd948c00afc751749a5a275b3a709436288da62f4cb36fb" \ + ";origin-pubkey=24873fcb06306adc8f65c8ecd5aaf7bc" \ + "6b62db8db50d8ce01677e49d48e3be50" \ + "af733b6e30a4c24fb52209cf6254b2b8" \ + "7c6ffb65a50583cea136a0488d431923" + +#define HBKZPC_FILE_ECDSA_DER "ecdsa.der" +#define HBKZPC_FILE_ECDSA_PEM "ecdsa.pem" +#define HBKZPC_FILE_ED25519_PEM "ed25519.pem" + +static const char *default_file_der = HBKZPC_FILE_ECDSA_DER; +static const char *default_file_pem = HBKZPC_FILE_ECDSA_PEM; + +enum rv { + PASS = 0, + SKIP = 77, + FAIL = 99, +}; + +static unsigned char text[32]; + +static unsigned char sha256[32]; + +static unsigned char *sig = NULL; +static size_t siglen = 0; + +static unsigned char *dgst_sig = NULL; +static size_t dgst_siglen = 0; + +static const char *file_der = NULL; +static const char *file_pem = NULL; + +/* helpers */ + +static const char *stringify(int rv) +{ + switch(rv) { + case PASS: + return "PASS"; + case FAIL: + return "FAIL"; + case SKIP: + return "SKIP"; + }; + return "n/a"; +} + +static EVP_PKEY *uri_pkey_get1(const char *uri) +{ + OSSL_STORE_CTX *sctx; + void *key = NULL; + + sctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (!sctx) { + fprintf(stderr, "fail: OSSL_STORE_open() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + while (!OSSL_STORE_eof(sctx)) { + OSSL_STORE_INFO *info = OSSL_STORE_load(sctx); + if (!info) { + fprintf(stderr, "fail: OSSL_STORE_load() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + switch (OSSL_STORE_INFO_get_type(info)) { + case OSSL_STORE_INFO_PKEY: + key = OSSL_STORE_INFO_get1_PKEY(info); + break; + default: + OSSL_STORE_INFO_free(info); + continue; + } + + OSSL_STORE_INFO_free(info); + break; + } +out_close: + OSSL_STORE_close(sctx); +out: + return key; +} + +static int sha2_256(unsigned char *in, size_t inlen) +{ + size_t len = sizeof(sha256); + + return EVP_Q_digest(NULL, "SHA2-256", NULL, + in, inlen, + sha256, &len); +} + +static int uri2file(const char *uri, const char *path, bool der) +{ + int rv = OSSL_RV_ERR; + ZPCHBK *zpchbk = NULL; + BIO *bo = NULL; + + if (!uri || !path) + goto out; + + zpchbk = ZPCHBK_new(); + if (!zpchbk) { + fprintf(stderr, "Unable to create hbk\n"); + goto out; + } + + ASN1_STRING_set(zpchbk->desc, HBKZPC_DER_DESC, -1); + ASN1_STRING_set(zpchbk->uri, uri, -1); + + bo = BIO_new_file(path, "w"); + if (!bo) { + fprintf(stderr, "Unable to open file: %s\n", path); + goto out; + } + + if (der) { + if (!i2d_ZPCHBK_bio(bo, zpchbk)) { + fprintf(stderr, "Unable to write DER file %s\n", path); + goto out; + } + } else { + if (!PEM_write_bio_ZPCHBK(bo, zpchbk)) { + fprintf(stderr, "Unable to write PEM file %s\n", path); + goto out; + } + } + + rv = OSSL_RV_OK; +out: + ZPCHBK_free(zpchbk); + BIO_free(bo); + return rv; +} + +static void reset_globals(void) +{ + OPENSSL_free(sig); + sig = NULL; + siglen = 0; + + OPENSSL_free(dgst_sig); + dgst_sig = NULL; + dgst_siglen = 0; +} + +/* tests */ + +static int test_provider(void) +{ + OSSL_LIB_CTX *libctx = NULL; + OSSL_PROVIDER *provider = NULL; + int rv = FAIL; + + provider = OSSL_PROVIDER_load(libctx, PROVIDER); + if (!provider) { + fprintf(stderr, "no provider %s\n", PROVIDER); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = PASS; +out: + return rv; +} + +static int test_store_load_eof(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + OSSL_STORE_INFO *info = NULL; + OSSL_STORE_CTX *sctx; + int rv = FAIL; + + sctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (!sctx) { + fprintf(stderr, "fail: OSSL_STORE_open() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + if (OSSL_STORE_eof(sctx) != 0) { + fprintf(stderr, "fail: OSSL_STORE_eof() != 0 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + info = OSSL_STORE_load(sctx); + if (!info) { + fprintf(stderr, "fail: OSSL_STORE_load() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + if (OSSL_STORE_eof(sctx) != 1) { + fprintf(stderr, "fail: OSSL_STORE_eof() != 1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + rv = PASS; +out_close: + OSSL_STORE_INFO_free(info); + OSSL_STORE_close(sctx); +out: + return rv; +} + +static int test_store_get_pkey(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + OSSL_STORE_CTX *sctx; + void *key = NULL; + int rv = FAIL; + + sctx = OSSL_STORE_open(uri, NULL, NULL, NULL, NULL); + if (!sctx) { + fprintf(stderr, "fail: OSSL_STORE_open() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + while (!OSSL_STORE_eof(sctx)) { + OSSL_STORE_INFO *info = OSSL_STORE_load(sctx); + if (!info) { + fprintf(stderr, "fail: OSSL_STORE_load() [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + switch (OSSL_STORE_INFO_get_type(info)) { + case OSSL_STORE_INFO_PUBKEY: + key = OSSL_STORE_INFO_get1_PUBKEY(info); + break; + case OSSL_STORE_INFO_PKEY: + key = OSSL_STORE_INFO_get1_PKEY(info); + break; + default: + OSSL_STORE_INFO_free(info); + continue; + } + + OSSL_STORE_INFO_free(info); + break; + } + + if (!key) { + fprintf(stderr, "fail: OSSL_STORE_INFO_[PUBKEY|PKEY] lookup [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out_close; + } + + EVP_PKEY_free(key); + rv = PASS; +out_close: + OSSL_STORE_close(sctx); +out: + return rv; +} + +static int test_signature_sign(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *signing_key; + EVP_PKEY_CTX *ctx; + int rv = FAIL; + + signing_key = uri_pkey_get1(uri); + if (!signing_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + ctx = EVP_PKEY_CTX_new(signing_key, NULL); + if (ctx == NULL) { + fprintf(stderr, "fail: EVP_PKEY_CTX_new [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto pkey_out; + } + + if (EVP_PKEY_sign_init(ctx) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_sign_init [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + /* Determine buffer length */ + if (EVP_PKEY_sign(ctx, NULL, &siglen, sha256, sizeof(sha256)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_sign [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + sig = OPENSSL_malloc(siglen); + if (sig == NULL) { + fprintf(stderr, "fail: malloc [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + if (EVP_PKEY_sign(ctx, sig, &siglen, sha256, sizeof(sha256)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_sign [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + OPENSSL_free(sig); + sig = NULL; + goto ctx_out; + } + + rv = PASS; +ctx_out: + EVP_PKEY_CTX_free(ctx); +pkey_out: + EVP_PKEY_free(signing_key); +out: + return rv; +} + +static int _test_signature_verify(unsigned char *s, size_t slen) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *verify_key; + EVP_PKEY_CTX *ctx; + int rv = FAIL; + + if (!sig || !siglen) + return SKIP; + + verify_key = uri_pkey_get1(uri); + if (!verify_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + ctx = EVP_PKEY_CTX_new(verify_key, NULL); + if (ctx == NULL) { + fprintf(stderr, "fail: EVP_PKEY_CTX_new [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto pkey_out; + } + + if (EVP_PKEY_verify_init(ctx) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_verify_init [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto ctx_out; + } + + if (EVP_PKEY_verify(ctx, s, slen, sha256, sizeof(sha256)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_PKEY_verify [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + OPENSSL_free(s); + s = NULL; + goto ctx_out; + } + + rv = PASS; +ctx_out: + EVP_PKEY_CTX_free(ctx); +pkey_out: + EVP_PKEY_free(verify_key); +out: + return rv; +} + +static int test_signature_verify(void) +{ + return _test_signature_verify(sig, siglen); +} + +static int test_signature_cross_verify(void) +{ + return _test_signature_verify(dgst_sig, dgst_siglen); +} + +static int _test_signature_digest_sign(EVP_PKEY *signing_key, + const char *msg) +{ + EVP_MD_CTX *md_ctx = NULL; + EVP_PKEY_CTX *ctx = NULL; + int rv = FAIL; + + /* just to make sure... */ + if (!signing_key) + return SKIP; + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + fprintf(stderr, "fail: EVP_MD_CTX_new [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + if (EVP_DigestSignInit_ex(md_ctx, &ctx, "SHA256", NULL, NULL, signing_key, NULL) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestSignInit_ex [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + if (EVP_DigestSignUpdate(md_ctx, text, sizeof(text)) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestSignUpdate [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + /* Determine buffer length */ + if (EVP_DigestSignFinal(md_ctx, NULL, &dgst_siglen) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestSignFinal [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + dgst_sig = OPENSSL_malloc(dgst_siglen); + if (dgst_sig == NULL) { + fprintf(stderr, "fail: malloc [%s]\n", msg); + ERR_print_errors_fp(stderr); + goto out; + } + + if (EVP_DigestSignFinal(md_ctx, dgst_sig, &dgst_siglen) != OSSL_RV_OK) { + fprintf(stderr, "fail: DigestSignFinal [%s]\n", msg); + ERR_print_errors_fp(stderr); + OPENSSL_free(dgst_sig); + dgst_sig = NULL; + goto out; + } + + rv = PASS; +out: + EVP_MD_CTX_free(md_ctx); + return rv; +} + +static int test_signature_digest_sign_uri(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *signing_key = NULL; + int rv = SKIP; + + signing_key = uri_pkey_get1(uri); + if (!signing_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = _test_signature_digest_sign(signing_key, "uri=" HBKZPC_URI_ECDSA_UV); +out: + EVP_PKEY_free(signing_key); + return rv; +} + +static int test_signature_digest_sign_pem(void) +{ + EVP_PKEY *signing_key = NULL; + BIO *bin = NULL; + int rv = SKIP; + + bin = BIO_new_file(default_file_pem, "r"); + if (!bin) { + fprintf(stderr, "fail: BIO_new_file [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + goto out; + } + + signing_key = PEM_read_bio_PrivateKey(bin, &signing_key, NULL, ""); + if (!signing_key) { + fprintf(stderr, "fail: PEM_read_bio_PrivateKey [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = _test_signature_digest_sign(signing_key, "path=" HBKZPC_FILE_ECDSA_PEM); +out: + EVP_PKEY_free(signing_key); + BIO_free(bin); + return rv; +} + +static int _test_signature_digest_verify(unsigned char *t, size_t tlen, + unsigned char *s, size_t slen) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + EVP_PKEY *verify_key; + EVP_MD_CTX *md_ctx; + EVP_PKEY_CTX *ctx; + int rv = FAIL; + + if (!s || !slen) + return SKIP; + + verify_key = uri_pkey_get1(uri); + if (!verify_key) { + fprintf(stderr, "fail: EVP_PKEY_get1 [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + md_ctx = EVP_MD_CTX_new(); + if (!md_ctx) { + fprintf(stderr, "fail: EVP_MD_CTX_new [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto pkey_out; + } + + if (EVP_DigestVerifyInit_ex(md_ctx, &ctx, "SHA256", NULL, NULL, verify_key, NULL) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestVerifyInit_ex [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto md_out; + } + + if (EVP_DigestVerifyUpdate(md_ctx, t, tlen) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestVerifyUpdate [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + goto md_out; + } + + if (EVP_DigestVerifyFinal(md_ctx, s, slen) != OSSL_RV_OK) { + fprintf(stderr, "fail: EVP_DigestVerifyFinal [uri=%s]\n", uri); + ERR_print_errors_fp(stderr); + s = NULL; + goto md_out; + } + + rv = PASS; +md_out: + EVP_MD_CTX_free(md_ctx); +pkey_out: + EVP_PKEY_free(verify_key); +out: + return rv; +} + +static int test_signature_digest_verify(void) +{ + return _test_signature_digest_verify(text, sizeof(text), dgst_sig, dgst_siglen); +} + +static int test_signature_cross_digest_verify(void) +{ + return _test_signature_digest_verify(text, sizeof(text), sig, siglen); +} + +static int test_uri_der_file(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + const char *file = default_file_der; + int rv = FAIL; + + rv = uri2file(uri, file, true) == OSSL_RV_OK ? PASS : FAIL; + if (rv == PASS) + file_der = file; + + return rv; +} + +static int test_uri_pem_file(void) +{ + const char *uri = HBKZPC_URI_ECDSA_UV; + const char *file = default_file_pem; + int rv = OSSL_RV_ERR; + + rv = uri2file(uri, file, false) == OSSL_RV_OK ? PASS : FAIL; + if (rv == PASS) + file_pem = file; + + return rv; +} + +static int test_pem_pkey(void) +{ + EVP_PKEY *signing_key = NULL; + BIO *bin = NULL; + int rv = FAIL; + + bin = BIO_new_file(default_file_pem, "r"); + if (!bin) { + fprintf(stderr, "fail: BIO_new_file [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + rv = SKIP; + goto out; + } + + signing_key = PEM_read_bio_PrivateKey(bin, &signing_key, NULL, ""); + if (!signing_key) { + fprintf(stderr, "fail: PEM_read_bio_PrivateKey [path=%s]\n", default_file_pem); + ERR_print_errors_fp(stderr); + goto out; + } + + rv = PASS; + EVP_PKEY_free(signing_key); +out: + BIO_free(bin); + return rv; +} + +#define RUNTEST(t) do { fprintf(stdout, "%s - %s\n", #t, stringify(t())); } while (0) + +int main(void) +{ + RAND_bytes(text, sizeof(text)); + sha2_256(text, sizeof(text)); + + RUNTEST(test_provider); + RUNTEST(test_store_load_eof); + RUNTEST(test_store_get_pkey); + RUNTEST(test_signature_sign); + RUNTEST(test_signature_verify); + RUNTEST(test_signature_digest_sign_uri); + RUNTEST(test_signature_digest_verify); + RUNTEST(test_signature_cross_verify); + RUNTEST(test_signature_cross_digest_verify); + + reset_globals(); + + RUNTEST(test_uri_der_file); + RUNTEST(test_uri_pem_file); + RUNTEST(test_pem_pkey); + RUNTEST(test_signature_digest_sign_pem); + RUNTEST(test_signature_digest_verify); + + reset_globals(); + + default_file_pem = HBKZPC_FILE_ED25519_PEM; + RUNTEST(test_pem_pkey); +} 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: + *; +};