From a6eda7f527cdf8fd2f9e5c69bd40849cae7e4c0a Mon Sep 17 00:00:00 2001 From: thb-sb Date: Thu, 14 Mar 2024 14:50:10 +0100 Subject: [PATCH] Fix #372: expose `hybrid_classical_` and `hybrid_pq_` `OSSL_PARAMS` for `EVP_PKEY`. This commit is an attempt to fix #372, by adding four new [`OSSL_PARAM`] to [`EVP_PKEY`]. The following [`OSSL_PARAM`] are added by this commit: - `hybrid_classical_pub`: an octet string to the classical public key. - `hybrid_classical_priv`: an octet string to the classical private key. - `hybrid_pq_pub`: an octet string to the quantum-resistant public key. - `hybrid_pq_priv`: an octet string to the quantum-resistant private key. Using [`EVP_PKEY_get_params`], OpenSSL users should be able to extract the specific subkey they want from an hybrid key. A test called `test_evp_pkey_params` has been added to ensure that it works with all hybrid algorithms, to also ensure that the output of these parameters are consistent between each other. [`OSSL_PARAM`]: https://www.openssl.org/docs/man3.2/man3/OSSL_PARAM.html [`EVP_PKEY`]: https://www.openssl.org/docs/man3.2/man7/evp.html [`EVP_PKEY_get_params`]: https://www.openssl.org/docs/man3.2/man3/EVP_PKEY_get_params.html Signed-off-by: thb-sb --- oqsprov/oqs_kmgmt.c | 111 +++++- oqsprov/oqs_prov.h | 1 + oqsprov/oqs_sig.c | 1 + oqsprov/oqsprov_keys.c | 1 + test/CMakeLists.txt | 21 ++ test/oqs_test_evp_pkey_params.c | 613 ++++++++++++++++++++++++++++++++ 6 files changed, 745 insertions(+), 3 deletions(-) create mode 100644 test/oqs_test_evp_pkey_params.c diff --git a/oqsprov/oqs_kmgmt.c b/oqsprov/oqs_kmgmt.c index a613a022..5dc344d1 100644 --- a/oqsprov/oqs_kmgmt.c +++ b/oqsprov/oqs_kmgmt.c @@ -314,9 +314,25 @@ static int oqsx_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, return ok; } -#define OQS_KEY_TYPES() \ - OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \ - OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0) +#define OSSL_HYBRID_PKEY_PARAM_CLASSICAL_PUB_KEY \ + "hybrid_classical_" OSSL_PKEY_PARAM_PUB_KEY +#define OSSL_HYBRID_PKEY_PARAM_CLASSICAL_PRIV_KEY \ + "hybrid_classical_" OSSL_PKEY_PARAM_PRIV_KEY +#define OSSL_HYBRID_PKEY_PARAM_PQ_PUB_KEY "hybrid_pq_" OSSL_PKEY_PARAM_PUB_KEY +#define OSSL_HYBRID_PKEY_PARAM_PQ_PRIV_KEY "hybrid_pq_" OSSL_PKEY_PARAM_PRIV_KEY + +#define OQS_HYBRID_KEY_TYPES() \ + OSSL_PARAM_octet_string(OSSL_HYBRID_PKEY_PARAM_CLASSICAL_PUB_KEY, NULL, \ + 0), \ + OSSL_PARAM_octet_string(OSSL_HYBRID_PKEY_PARAM_CLASSICAL_PRIV_KEY, \ + NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_HYBRID_PKEY_PARAM_PQ_PUB_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_HYBRID_PKEY_PARAM_PQ_PRIV_KEY, NULL, 0) + +#define OQS_KEY_TYPES() \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \ + OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0), \ + OQS_HYBRID_KEY_TYPES() static const OSSL_PARAM oqsx_key_types[] = {OQS_KEY_TYPES(), OSSL_PARAM_END}; static const OSSL_PARAM *oqs_imexport_types(int selection) @@ -327,6 +343,92 @@ static const OSSL_PARAM *oqs_imexport_types(int selection) return NULL; } +// Tells if a key (SIG, KEM, ECP_HYB_KEM, ECX_HYB_KEM or HYB_SIG) is using +// hybrid algorithm. +// +// Returns 1 if hybrid, else 0. +static int oqsx_key_is_hybrid(const OQSX_KEY *oqsxk) +{ + if ((oqsxk->keytype == KEY_TYPE_ECP_HYB_KEM + || oqsxk->keytype == KEY_TYPE_ECX_HYB_KEM + || oqsxk->keytype == KEY_TYPE_HYB_SIG) + && oqsxk->numkeys == 2 && oqsxk->classical_pkey != NULL) { + OQS_KM_PRINTF("OQSKEYMGMT: key is hybrid\n"); + return 1; + } + return 0; +} + +// Gets the classical params of an hybrid key. + +// Gets hybrid params. +// +// Returns 0 on success. +static int oqsx_get_hybrid_params(OQSX_KEY *key, OSSL_PARAM params[]) +{ + OSSL_PARAM *p; + const void *classical_pubkey = NULL; + const void *classical_privkey = NULL; + const void *pq_pubkey = NULL; + const void *pq_privkey = NULL; + int classical_pubkey_len = 0; + int classical_privkey_len = 0; + int pq_pubkey_len = 0; + int pq_privkey_len = 0; + + if (oqsx_key_is_hybrid(key) != 1) + return 0; + + if (key->numkeys != 2) { + OQS_KM_PRINTF2("OQSKEYMGMT: key is hybrid but key->numkeys = %zu\n", + key->numkeys); + ERR_raise(ERR_LIB_PROV, OQSPROV_R_INTERNAL_ERROR); + return -1; + } + + if (key->comp_pubkey != NULL && key->pubkey != NULL) { + classical_pubkey = key->comp_pubkey[0]; + DECODE_UINT32(classical_pubkey_len, key->pubkey); + } + if (key->comp_privkey != NULL && key->privkey != NULL) { + classical_privkey = key->comp_privkey[0]; + DECODE_UINT32(classical_privkey_len, key->privkey); + } + + if (key->comp_pubkey[1] != NULL) { + pq_pubkey = key->comp_pubkey[1]; + pq_pubkey_len = key->pubkeylen - classical_pubkey_len - SIZE_OF_UINT32; + } + if (key->comp_privkey != NULL) { + pq_privkey = key->comp_privkey[1]; + pq_privkey_len + = key->privkeylen - classical_privkey_len - SIZE_OF_UINT32; + } + + if ((p + = OSSL_PARAM_locate(params, OSSL_HYBRID_PKEY_PARAM_CLASSICAL_PUB_KEY)) + != NULL + && !OSSL_PARAM_set_octet_string(p, classical_pubkey, + classical_pubkey_len)) + return -1; + if ((p + = OSSL_PARAM_locate(params, OSSL_HYBRID_PKEY_PARAM_CLASSICAL_PRIV_KEY)) + != NULL + && !OSSL_PARAM_set_octet_string(p, classical_privkey, + classical_privkey_len)) + return -1; + if ((p = OSSL_PARAM_locate(params, OSSL_HYBRID_PKEY_PARAM_PQ_PUB_KEY)) + != NULL + && !OSSL_PARAM_set_octet_string(p, pq_pubkey, pq_pubkey_len)) + return -1; + if ((p = OSSL_PARAM_locate(params, OSSL_HYBRID_PKEY_PARAM_PQ_PRIV_KEY)) + != NULL + && !OSSL_PARAM_set_octet_string(p, pq_privkey, pq_privkey_len)) + return -1; + + return 0; +} + // must handle param requests for KEM and SIG keys... static int oqsx_get_params(void *key, OSSL_PARAM params[]) { @@ -384,6 +486,9 @@ static int oqsx_get_params(void *key, OSSL_PARAM params[]) return 0; } + if (oqsx_get_hybrid_params(oqsxk, params)) + return 0; + // not passing in params to respond to is no error return 1; } diff --git a/oqsprov/oqs_prov.h b/oqsprov/oqs_prov.h index 847e6f65..80046a66 100644 --- a/oqsprov/oqs_prov.h +++ b/oqsprov/oqs_prov.h @@ -47,6 +47,7 @@ #define OQSPROV_R_WRONG_PARAMETERS 13 #define OQSPROV_R_VERIFY_ERROR 14 #define OQSPROV_R_EVPINFO_MISSING 15 +#define OQSPROV_R_INTERNAL_ERROR 16 /* Extras for OQS extension */ diff --git a/oqsprov/oqs_sig.c b/oqsprov/oqs_sig.c index 86a4ae3a..3b553fc9 100644 --- a/oqsprov/oqs_sig.c +++ b/oqsprov/oqs_sig.c @@ -19,6 +19,7 @@ #include #include #include +#include #include // TBD: Review what we really need/want: For now go with OSSL settings: diff --git a/oqsprov/oqsprov_keys.c b/oqsprov/oqsprov_keys.c index 53e96a07..8e33ef6b 100644 --- a/oqsprov/oqsprov_keys.c +++ b/oqsprov/oqsprov_keys.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1c5fd96a..d73119ba 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -110,11 +110,32 @@ set_tests_properties(oqs_endecode ) endif() +add_executable(oqs_test_evp_pkey_params oqs_test_evp_pkey_params.c test_common.c) +target_link_libraries(oqs_test_evp_pkey_params PRIVATE ${OPENSSL_CRYPTO_LIBRARY} ${OQS_ADDL_SOCKET_LIBS}) +add_test( + NAME oqs_evp_pkey_params + COMMAND oqs_test_evp_pkey_params + "oqsprovider" + "${CMAKE_CURRENT_SOURCE_DIR}/openssl-ca.cnf" +) +# openssl under MSVC seems to have a bug registering NIDs: +# It only works when setting OPENSSL_CONF, not when loading the same cnf file: +if (MSVC) +set_tests_properties(oqs_evp_pkey_params + PROPERTIES ENVIRONMENT "OPENSSL_MODULES=${OQS_PROV_BINARY_DIR};OPENSSL_CONF=${CMAKE_CURRENT_SOURCE_DIR}/openssl-ca.cnf" +) +else() +set_tests_properties(oqs_evp_pkey_params + PROPERTIES ENVIRONMENT "OPENSSL_MODULES=${OQS_PROV_BINARY_DIR}" +) +endif() + if (OQS_PROVIDER_BUILD_STATIC) targets_set_static_provider(oqs_test_signatures oqs_test_kems oqs_test_groups oqs_test_tlssig oqs_test_endecode + oqs_test_evp_pkey_params ) endif() diff --git a/test/oqs_test_evp_pkey_params.c b/test/oqs_test_evp_pkey_params.c new file mode 100644 index 00000000..616be64a --- /dev/null +++ b/test/oqs_test_evp_pkey_params.c @@ -0,0 +1,613 @@ +// SPDX-License-Identifier: Apache-2.0 AND MIT + +#include "test_common.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/** \brief List of hybrid signature algorithms. */ +const char *kHybridSignatureAlgorithms[] = { + "p256_dilithium2", + "rsa3072_dilithium2", + "p384_dilithium3", + "p521_dilithium5", + "p256_mldsa44", + "rsa3072_mldsa44", + "p384_mldsa65", + "p521_mldsa87", + "p256_falcon512", + "rsa3072_falcon512", + "p256_falconpadded512", + "rsa3072_falconpadded512", + "p521_falcon1024", + "p521_falconpadded1024", + "p256_sphincssha2128fsimple", + "rsa3072_sphincssha2128fsimple", + "p256_sphincssha2128ssimple", + "rsa3072_sphincssha2128ssimple", + "p384_sphincssha2192fsimple", + "p256_sphincsshake128fsimple", + "rsa3072_sphincsshake128fsimple", + NULL, +}; + +/** \brief List of hybrid KEMs. */ +const char *kHybridKEMAlgorithms[] = { + "p256_frodo640aes", "x25519_frodo640aes", "p256_frodo640shake", + "x25519_frodo640shake", "p384_frodo976aes", "x448_frodo976aes", + "p384_frodo976shake", "x448_frodo976shake", "p521_frodo1344aes", + "p521_frodo1344shake", "p256_kyber512", "x25519_kyber512", + "p384_kyber768", "x448_kyber768", "x25519_kyber768", + "p256_kyber768", "p521_kyber1024", "p256_mlkem512", + "x25519_mlkem512", "p384_mlkem768", "x448_mlkem768", + "x25519_mlkem768", "p256_mlkem768", "p521_mlkem1024", + "p384_mlkem1024", "p256_bikel1", "x25519_bikel1", + "p384_bikel3", "x448_bikel3", "p521_bikel5", + "p256_hqc128", "x25519_hqc128", "p384_hqc192", + "x448_hqc192", "p521_hqc256", NULL, +}; + +/** \brief Indicates if a string is in a given list of strings. + * + * \param list List of strings. + * \param s String to test. + * + * \return 1 if `s` is in `list`, else 0. */ +static int is_string_in_list(const char **list, const char *s) +{ + for (; *list != NULL && strcmp(*list, s) != 0; ++list) + ; + if (*list != NULL) { + return 1; + } + return 0; +} + +/** \brief Indicates if a signature algorithm is hybrid or not. + * + * \param alg Algorithm name. + * + * \returns 1 if hybrid, else 0. */ +#define is_signature_algorithm_hybrid(_alg_) \ + is_string_in_list(kHybridSignatureAlgorithms, (_alg_)) + +/** \brief Indicates if an kem algorithm is hybrid or not. + * + * \param alg Algorithm name. + * + * \returns 1 if hybrid, else 0. */ +#define is_kem_algorithm_hybrid(_alg_) \ + is_string_in_list(kHybridKEMAlgorithms, (_alg_)) + +/** \brief A pair of keys. */ +struct KeyPair { + /** \brief The public key. */ + uint8_t *pubkey; + + /** \brief The public key length, in bytes. */ + size_t pubkey_len; + + /** \brief The private key. */ + uint8_t *privkey; + + /** \brief The private key length, in bytes. */ + size_t privkey_len; + + /** \brief Indicates if the pair of keys is from a quantum-resistant + * algorithm (1) or not (0). */ + int is_pq; +}; + +/** \brief Frees the memory occupied by a KeyPair. + * + * \param kp Keypair to free. */ +static void keypair_free(struct KeyPair *kp) +{ + free(kp->pubkey); + free(kp->privkey); +} + +/** \brief Initializes an OpenSSL top-level context. + * + * \returns The top-level context, or `NULL` if an error occurred. */ +static OSSL_LIB_CTX *init_openssl(void) +{ + OSSL_LIB_CTX *ctx; + + if (!(ctx = OSSL_LIB_CTX_new())) { + fputs("failed to initialize a new `OSSL_LIB_CTX`\n", stderr); + } + + return ctx; +} + +/** \brief Loads the default provider. + * + * \param libctx Top-level OpenSSL context. + * + * \returns The default provider, or `NULL` if an error occurred. */ +static OSSL_PROVIDER *load_default_provider(OSSL_LIB_CTX *libctx) +{ + OSSL_PROVIDER *provider; + + if (!(provider = OSSL_PROVIDER_load(libctx, "default"))) { + fputs("failed to load the `default` provider: ", stderr); + ERR_print_errors_fp(stderr); + fputc('\n', stderr); + } + + return provider; +} + +/** \brief Initializes a context for the EVP_PKEY API. + * + * \param libctx Top-level OpenSSL context. + * \paran alg The algorithm to use. + * + * \returns The EVP_PKEY context, or `NULL` if an error occurred. */ +static EVP_PKEY_CTX *init_EVP_PKEY_CTX(OSSL_LIB_CTX *libctx, const char *alg) +{ + EVP_PKEY_CTX *ctx; + + if (!(ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, NULL))) { + fprintf(stderr, + "`EVP_PKEY_CTX_new_from_name` failed with algorithm %s: ", alg); + ERR_print_errors_fp(stderr); + fputc('\n', stderr); + } + + return ctx; +} + +/** \brief Initializes the keygen operation on an EVP_PKEY context. + * + * \param ctx EVP_PKEY context. + * + * \returns 0 on success. */ +static int init_keygen(EVP_PKEY_CTX *ctx) +{ + int err; + + if ((err = EVP_PKEY_keygen_init(ctx)) == -2) { + fputs("`EVP_PKEY_keygen_init` failed, couldn't initialize keygen: not " + "supported\n", + stderr); + } else if (err <= 0) { + fputs("`EVP_PKEY_keygen_init` failed, couldn't initialize keygen: ", + stderr); + ERR_print_errors_fp(stderr); + fputc('\n', stderr); + } + + return err; +} + +/** \brief Generates the private key. + * + * \param ctx EVP_PKEY context. + * + * \returns The private key, or `NULL` if an error occurred. */ +static EVP_PKEY *generate_private_key(EVP_PKEY_CTX *ctx) +{ + EVP_PKEY *private_key = NULL; + int err; + + if ((err = EVP_PKEY_generate(ctx, &private_key)) == -2) { + fputs("`EVP_PKEY_generate` failed, couldn't generate: not supported\n", + stderr); + } else if (err <= 0) { + fputs("`EVP_PKEY_generate` failed, couldn't generate: ", stderr); + ERR_print_errors_fp(stderr); + fputc('\n', stderr); + } + + return private_key; +} + +/** \brief Extracts an octet string from a parameter of an EVP_PKEY. + * + * \param key The EVP_PKEY; + * \param param_name Name of the parameter. + * \param[out] buf Out buffer. + * \param[out] buf_len Size of out buffer. + * + * \returns 0 on success. */ +static int get_param_octet_string(const EVP_PKEY *key, const char *param_name, + uint8_t **buf, size_t *buf_len) +{ + *buf = NULL; + *buf_len = 0; + int ret = -1; + + if (EVP_PKEY_get_octet_string_param(key, param_name, NULL, 0, buf_len) + != 1) { + fprintf(stderr, + "`EVP_PKEY_get_octet_string_param` failed with param `%s`: ", + param_name); + ERR_print_errors_fp(stderr); + fputc('\n', stderr); + goto out; + } + if (!(*buf = malloc(*buf_len))) { + fprintf(stderr, "failed to allocate %#zx byte(s)\n", *buf_len); + goto out; + } + if (EVP_PKEY_get_octet_string_param(key, param_name, *buf, *buf_len, + buf_len) + != 1) { + fprintf(stderr, + "`EVP_PKEY_get_octet_string_param` failed with param `%s`: ", + param_name); + ERR_print_errors_fp(stderr); + fputc('\n', stderr); + free(*buf); + *buf = NULL; + } else { + ret = 0; + } + +out: + return ret; +} + +/** \brief Extracts the classical keys from an hybrid key. + * + * \param private_key The private key. + * \param[out] out Key pair where to write the keys. + * + * \returns 0 on success. */ +static int private_key_params_get_classical_keys(const EVP_PKEY *private_key, + struct KeyPair *out) +{ + int ret = -1; + + if (get_param_octet_string(private_key, "hybrid_classical_pub", + &out->pubkey, &out->pubkey_len)) { + goto out; + } + if (get_param_octet_string(private_key, "hybrid_classical_priv", + &out->privkey, &out->privkey_len)) { + goto free_pubkey; + } + ret = 0; + goto out; + +free_pubkey: + free(out->pubkey); + +out: + return ret; +} + +/** \brief Extracts the quantum-resistant keys from an hybrid key. + * + * \param private_key The private key. + * \param[out] out Key pair where to write the keys. + * + * \returns 0 on success. */ +static int private_key_params_get_pq_keys(const EVP_PKEY *private_key, + struct KeyPair *out) +{ + int ret = -1; + + if (get_param_octet_string(private_key, "hybrid_pq_pub", &out->pubkey, + &out->pubkey_len)) { + goto out; + } + if (get_param_octet_string(private_key, "hybrid_pq_priv", &out->privkey, + &out->privkey_len)) { + goto free_pubkey; + } + ret = 0; + goto out; + +free_pubkey: + free(out->pubkey); + +out: + return ret; +} + +/** \brief Extracts the combination of classical+hybrid keys from an hybrid key. + * + * \param private_key The private key. + * \param[out] out Key pair where to write the keys. + * + * \returns 0 on success. */ +static int private_key_params_get_full_keys(const EVP_PKEY *private_key, + struct KeyPair *out) +{ + int ret = -1; + + if (get_param_octet_string(private_key, OSSL_PKEY_PARAM_PUB_KEY, + &out->pubkey, &out->pubkey_len)) { + goto out; + } + if (get_param_octet_string(private_key, OSSL_PKEY_PARAM_PRIV_KEY, + &out->privkey, &out->privkey_len)) { + goto free_pubkey; + } + ret = 0; + goto out; + +free_pubkey: + free(out->pubkey); + +out: + return ret; +} + +/** \brief Reconstitutes the combination of a classical key and a + * quantum-resistant key. + * + * \param classical Classical key. + * \param classical_n Length in bytes of `classical`. + * \param pq Quantum-resistant key. + * \param pq_n Length in bytes of `pq`. + * \param[out] buf Out buffer. + * \param[out] buf_n Length in bytes of `buf`. + * + * \returns 0 on success. */ +static int reconstitute_keys(const uint8_t *classical, const size_t classical_n, + const uint8_t *pq, const size_t pq_n, + uint8_t **buf, size_t *buf_len) +{ + uint32_t header; + int ret = -1; + + *buf_len = sizeof(uint32_t) + classical_n + pq_n; + if (!(*buf = malloc(*buf_len))) { + fprintf(stderr, "failed to allocate %#zx byte(s)\n", *buf_len); + goto out; + } + header = classical_n; + (*buf)[0] = header >> 0x18; + (*buf)[1] = header >> 0x10; + (*buf)[2] = header >> 0x8; + (*buf)[3] = header; + memcpy(*buf + sizeof(header), classical, classical_n); + memcpy(*buf + sizeof(header) + classical_n, pq, pq_n); + ret = 0; + +out: + return ret; +} + +/** \brief Dump a buffer in hex. + * + * \param buf Buffer to dump. + * \param n Length in bytes of `buf`. + * \param stream Stream where to write the dump. */ +static void dump_buffer(const uint8_t *buf, const size_t n, FILE *stream) +{ + const uint8_t *end = buf + n; + for (; buf != end; ++buf) { + fprintf(stream, "%02hhx", *buf); + } +} + +/** \brief Verifies the consistency between pairs of keys. + * + * \param classical The classical keypair. + * \param pq The quantum-resistant keypair. + * \param comb The combination of both classical+quantum-resistant keypairs. + * + * \returns 0 on success. */ +static int keypairs_verify_consistency(const struct KeyPair *classical, + const struct KeyPair *pq, + const struct KeyPair *comb) +{ + uint8_t *reconstitution; + size_t n; + int ret = -1; + + if (reconstitute_keys(classical->pubkey, classical->pubkey_len, pq->pubkey, + pq->pubkey_len, &reconstitution, &n)) { + goto out; + } + if (n != comb->pubkey_len) { + fprintf( + stderr, + "expected %#zx byte(s) for reconstitution of pubkey, got %#zx\n", + comb->pubkey_len, n); + goto free_reconstitute; + } + if (memcmp(reconstitution, comb->pubkey, n)) { + fputs("pubkey and comb->pubkey differ\n", stderr); + fputs("pubkey: ", stderr); + dump_buffer(reconstitution, n, stderr); + fputs("\ncomb->pubkey: ", stderr); + dump_buffer(comb->pubkey, n, stderr); + fputc('\n', stderr); + goto free_reconstitute; + } + free(reconstitution); + + if (reconstitute_keys(classical->privkey, classical->privkey_len, + pq->privkey, pq->privkey_len, &reconstitution, &n)) { + goto out; + } + if (n != comb->privkey_len) { + fprintf( + stderr, + "expected %#zx byte(s) for reconstitution of privkey, got %#zx\n", + comb->privkey_len, n); + goto free_reconstitute; + } + if (memcmp(reconstitution, comb->privkey, n)) { + fputs("privkey and comb->privkey differ\n", stderr); + fputs("privkey: ", stderr); + dump_buffer(reconstitution, n, stderr); + fputs("\ncomb->privkey: ", stderr); + dump_buffer(comb->privkey, n, stderr); + fputc('\n', stderr); + goto free_reconstitute; + } + puts("consistency is OK"); + ret = 0; + +free_reconstitute: + free(reconstitution); + +out: + return ret; +} + +/** \brief Tests an algorithm. + * + * \param libctx Top-level OpenSSL context. + * \param algname Algorithm name. + * + * \returns 0 on success. */ +static int test_algorithm(OSSL_LIB_CTX *libctx, const char *algname) +{ + EVP_PKEY_CTX *evp_pkey_ctx; + EVP_PKEY *private_key; + struct KeyPair classical_keypair; + struct KeyPair pq_keypair; + struct KeyPair full_keypair; + int ret = -1; + + if (!(evp_pkey_ctx = init_EVP_PKEY_CTX(libctx, algname))) { + goto out; + } + + if (init_keygen(evp_pkey_ctx) != 1) { + goto free_evp_pkey_ctx; + } + + if (!(private_key = generate_private_key(evp_pkey_ctx))) { + goto free_evp_pkey_ctx; + } + + if (private_key_params_get_classical_keys(private_key, + &classical_keypair)) { + goto free_private_key; + } + + if (private_key_params_get_pq_keys(private_key, &pq_keypair)) { + goto free_classical_keypair; + } + + if (private_key_params_get_full_keys(private_key, &full_keypair)) { + goto free_pq_keypair; + } + + if (!keypairs_verify_consistency(&classical_keypair, &pq_keypair, + &full_keypair)) { + ret = 0; + } + + keypair_free(&full_keypair); + +free_pq_keypair: + keypair_free(&pq_keypair); + +free_classical_keypair: + keypair_free(&classical_keypair); + +free_private_key: + EVP_PKEY_free(private_key); + +free_evp_pkey_ctx: + EVP_PKEY_CTX_free(evp_pkey_ctx); + +out: + return ret; +} + +int main(int argc, char **argv) +{ + OSSL_LIB_CTX *libctx; + OSSL_PROVIDER *default_provider; + OSSL_PROVIDER *oqs_provider; + const char *modulename; + const char *configfile; + const OSSL_ALGORITHM *algs; + int query_nocache; + int errcnt; + int ret = EXIT_FAILURE; + + if (!(libctx = init_openssl())) { + goto end; + } + + if (!(default_provider = load_default_provider(libctx))) { + goto free_libctx; + } + + T(argc == 3); + modulename = argv[1]; + configfile = argv[2]; + + load_oqs_provider(libctx, modulename, configfile); + if (!(oqs_provider = OSSL_PROVIDER_load(libctx, modulename))) { + fputs(cRED " `oqs_provider` is NULL " cNORM "\n", stderr); + goto unload_default_provider; + } + + errcnt = 0; + algs = OSSL_PROVIDER_query_operation(oqs_provider, OSSL_OP_SIGNATURE, + &query_nocache); + if (!algs) { + fprintf(stderr, cRED " No signature algorithms found" cNORM "\n"); + ERR_print_errors_fp(stderr); + ++errcnt; + } + + for (; algs->algorithm_names != NULL; ++algs) { + if (!is_signature_algorithm_hybrid(algs->algorithm_names)) { + continue; + } + fprintf(stderr, "testing %s\n", algs->algorithm_names); + if (test_algorithm(libctx, algs->algorithm_names)) { + fprintf(stderr, cRED " failed for %s " cNORM "\n", + algs->algorithm_names); + ++errcnt; + } + } + + algs = OSSL_PROVIDER_query_operation(oqs_provider, OSSL_OP_KEM, + &query_nocache); + if (!algs) { + fprintf(stderr, cRED " No KEM algorithms found" cNORM "\n"); + ERR_print_errors_fp(stderr); + ++errcnt; + goto unload_oqs_provider; + } + for (; algs->algorithm_names != NULL; ++algs) { + if (!is_kem_algorithm_hybrid(algs->algorithm_names)) { + continue; + } + fprintf(stderr, "testing %s\n", algs->algorithm_names); + if (test_algorithm(libctx, algs->algorithm_names)) { + fprintf(stderr, cRED " failed for %s " cNORM "\n", + algs->algorithm_names); + ++errcnt; + } + } + + if (errcnt == 0) { + ret = EXIT_SUCCESS; + } + +unload_oqs_provider: + OSSL_PROVIDER_unload(oqs_provider); + +unload_default_provider: + OSSL_PROVIDER_unload(default_provider); + +free_libctx: + OSSL_LIB_CTX_free(libctx); + +end: + return ret; +} \ No newline at end of file