From 91ca50f701a003a01d4429e334b3a12e38bb2ec1 Mon Sep 17 00:00:00 2001 From: Jake Massimo Date: Tue, 7 May 2024 10:28:37 -0700 Subject: [PATCH] connected deterministic ml-kem modes to experimental EVP API --- crypto/evp_extra/evp_extra_test.cc | 105 ++++++++++++++++++++++++++ crypto/evp_extra/p_ed25519.c | 2 + crypto/evp_extra/p_kem.c | 91 ++++++++++++++++++++++ crypto/evp_extra/p_x25519.c | 2 + crypto/fipsmodule/evp/evp_ctx.c | 59 +++++++++++++++ crypto/fipsmodule/evp/internal.h | 15 +++- crypto/kem/internal.h | 9 +++ crypto/kem/kem_methods.c | 21 ++++++ crypto/ml_kem/ml_kem.c | 13 ++++ crypto/ml_kem/ml_kem.h | 9 +++ include/openssl/experimental/ml-kem.h | 64 ++++++++++++++++ 11 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 include/openssl/experimental/ml-kem.h diff --git a/crypto/evp_extra/evp_extra_test.cc b/crypto/evp_extra/evp_extra_test.cc index ca51339e6dd..6c5c59ddaf0 100644 --- a/crypto/evp_extra/evp_extra_test.cc +++ b/crypto/evp_extra/evp_extra_test.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include "../rand_extra/pq_custom_randombytes.h" #include "../test/file_test.h" @@ -2674,3 +2675,107 @@ TEST_P(PerKEMTest, KAT) { EXPECT_EQ(Bytes(ss_expected), Bytes(ss)); }); } + +//TEST FUNCTIONS FOR DETERMINISTIC APIS +// these will be condensed with the non-deterministic KEM test functions once a full +// migration from kyberR3 and the current pq_custom_randombytes_init_for_testing +// mechanisms have been moved. For now, we test functionality of the deterministic +// APIs separately. + +static bssl::UniquePtr setup_ctx_and_generate_key_deterministic(int kem_nid, const uint8_t * seed) { + + // Create context of KEM type. + bssl::UniquePtr ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_KEM, nullptr)); + EXPECT_TRUE(ctx); + + // Set up the context with specific KEM parameters. + EXPECT_TRUE(EVP_PKEY_CTX_kem_set_params(ctx.get(), kem_nid)); + + // Generate a key pair. + EVP_PKEY *raw = nullptr; + EXPECT_TRUE(EVP_PKEY_keygen_init(ctx.get())); + EXPECT_TRUE(EVP_PKEY_keygen_deterministic(ctx.get(), &raw, seed)); + EXPECT_TRUE(raw); + + // Create PKEY from the generated raw key and a new context with it. + bssl::UniquePtr pkey(raw); + ctx.reset(EVP_PKEY_CTX_new(pkey.get(), nullptr)); + EXPECT_TRUE(ctx); + + return ctx; +} + +static const struct KnownKEM kKEMs_deterministic[] = { + //{"MLKEM512IPD", NID_MLKEM512IPD, 800, 1632, 768, 32, "ml_kem/kat/mlkem512ipd.txt"}, //these can be re-added once KATs are re-generated + //{"Kyber512r3", NID_KYBER512_R3, 800, 1632, 768, 32, "ml_kem/kat/mlkem512ipd.txt"}, + //{"Kyber512r3", NID_KYBER512_R3, 800, 1632, 768, 32, "kyber/kat/kyber512r3.txt"}, + {"MLKEM512IPD", NID_MLKEM512IPD, 800, 1632, 768, 32, "ml_kem/kat/mlkem512ipd.txt"}, +}; + +class PerKEMTest_deterministic : public testing::TestWithParam {}; + +INSTANTIATE_TEST_SUITE_P(All, PerKEMTest_deterministic, testing::ValuesIn(kKEMs_deterministic), + [](const testing::TestParamInfo ¶ms) + -> std::string { return params.param.name; }); + +TEST_P(PerKEMTest_deterministic, KAT) { + std::string kat_filepath = "crypto/"; + kat_filepath += GetParam().kat_filename; + + FileTestGTest(kat_filepath.c_str(), [&](FileTest *t) { + std::string count; + std::vector seed, pk_expected, sk_expected, ct_expected, ss_expected; + + ASSERT_TRUE(t->GetAttribute(&count, "count")); + ASSERT_TRUE(t->GetBytes(&seed, "seed")); + ASSERT_TRUE(t->GetBytes(&pk_expected, "pk")); + ASSERT_TRUE(t->GetBytes(&sk_expected, "sk")); + ASSERT_TRUE(t->GetBytes(&ct_expected, "ct")); + ASSERT_TRUE(t->GetBytes(&ss_expected, "ss")); + + size_t pk_len = GetParam().public_key_len; + size_t sk_len = GetParam().secret_key_len; + size_t ct_len = GetParam().ciphertext_len; + size_t ss_len = GetParam().shared_secret_len; + + // Set randomness generation in deterministic mode. + // This will be removed in a subsequent PR, the first stage is removing this + // from inside the kyber code to here. The second phase is to re-do the + // KATs to output coins not seeds, we can then finally remove + // pq_custom_randombytes completely. + + pq_custom_randombytes_use_deterministic_for_testing(); + pq_custom_randombytes_init_for_testing(seed.data()); + + // get random bytes from the PRNG (this is potentially KEM specific - TBD) + uint8_t buf[2*32]; + pq_custom_randombytes(buf, 2*32); + + // ---- 1. Setup the context and generate the key ---- + bssl::UniquePtr ctx; + ctx = setup_ctx_and_generate_key_deterministic(GetParam().nid, buf); + ASSERT_TRUE(ctx); + + EVP_PKEY *pkey = EVP_PKEY_CTX_get0_pkey(ctx.get()); + ASSERT_TRUE(pkey); + CMP_VEC_AND_PKEY_PUBLIC(pk_expected, pkey, pk_len); + CMP_VEC_AND_PKEY_SECRET(sk_expected, pkey, sk_len); + + // ---- 2. Encapsulation ---- + std::vector ct(ct_len); + std::vector ss(ss_len); + + // get random bytes from the PRNG (this is potentially KEM specific - TBD) + uint8_t buf2[32]; + pq_custom_randombytes(buf2, 32); + + ASSERT_TRUE(EVP_PKEY_encapsulate_deterministic(ctx.get(), ct.data(), &ct_len, ss.data(), &ss_len, buf2)); + EXPECT_EQ(Bytes(ct_expected), Bytes(ct)); + EXPECT_EQ(Bytes(ss_expected), Bytes(ss)); + + // ---- 3. Decapsulation ---- + ASSERT_TRUE(EVP_PKEY_decapsulate(ctx.get(), ss.data(), &ss_len, ct.data(), ct_len)); + EXPECT_EQ(Bytes(ss_expected), Bytes(ss)); + }); +} + diff --git a/crypto/evp_extra/p_ed25519.c b/crypto/evp_extra/p_ed25519.c index 32008631e89..4b437b111dc 100644 --- a/crypto/evp_extra/p_ed25519.c +++ b/crypto/evp_extra/p_ed25519.c @@ -103,6 +103,8 @@ const EVP_PKEY_METHOD ed25519_pkey_meth = { NULL /* derive */, NULL /* paramgen */, NULL /* ctrl */, + NULL /* keygen deterministic */, + NULL /* encapsulate deterministic */, NULL /* encapsulate */, NULL /* decapsulate */, }; diff --git a/crypto/evp_extra/p_kem.c b/crypto/evp_extra/p_kem.c index dd97ca9a668..63f14b81235 100644 --- a/crypto/evp_extra/p_kem.c +++ b/crypto/evp_extra/p_kem.c @@ -32,6 +32,31 @@ static void pkey_kem_cleanup(EVP_PKEY_CTX *ctx) { OPENSSL_free(ctx->data); } +static int pkey_kem_keygen_deterministic(EVP_PKEY_CTX *ctx, + EVP_PKEY *pkey, + const uint8_t *seed) { + KEM_PKEY_CTX *dctx = ctx->data; + const KEM *kem = dctx->kem; + if (kem == NULL) { + if (ctx->pkey == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET); + return 0; + } + kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key); + } + + KEM_KEY *key = KEM_KEY_new(); + if (key == NULL || + !KEM_KEY_init(key, kem) || + !kem->method->keygen_deterministic(key->public_key, key->secret_key, seed) || + !EVP_PKEY_assign(pkey, EVP_PKEY_KEM, key)) { + KEM_KEY_free(key); + return 0; + } + + return 1; +} + static int pkey_kem_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) { KEM_PKEY_CTX *dctx = ctx->data; const KEM *kem = dctx->kem; @@ -55,6 +80,70 @@ static int pkey_kem_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) { return 1; } +static int pkey_kem_encapsulate_deterministic(EVP_PKEY_CTX *ctx, + uint8_t *ciphertext, + size_t *ciphertext_len, + uint8_t *shared_secret, + size_t *shared_secret_len, + const uint8_t *seed) { + KEM_PKEY_CTX *dctx = ctx->data; + const KEM *kem = dctx->kem; + if (kem == NULL) { + if (ctx->pkey == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_NO_PARAMETERS_SET); + return 0; + } + kem = KEM_KEY_get0_kem(ctx->pkey->pkey.kem_key); + } + + // Caller is getting parameter values. + if (ciphertext == NULL && shared_secret == NULL) { + *ciphertext_len = kem->ciphertext_len; + *shared_secret_len = kem->shared_secret_len; + return 1; + } + + // If not getting parameter values, then both + // output buffers need to be valid (non-NULL) + if (ciphertext == NULL || shared_secret == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PARAMETERS); + return 0; + } + + // The output buffers need to be large enough. + if (*ciphertext_len < kem->ciphertext_len || + *shared_secret_len < kem->shared_secret_len) { + OPENSSL_PUT_ERROR(EVP, EVP_R_BUFFER_TOO_SMALL); + return 0; + } + + // Check that the context is properly configured. + if (ctx->pkey == NULL || + ctx->pkey->pkey.kem_key == NULL || + ctx->pkey->type != EVP_PKEY_KEM) { + OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED); + return 0; + } + + // Check that the key has a public key set. + KEM_KEY *key = ctx->pkey->pkey.kem_key; + if (key->public_key == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_NO_KEY_SET); + return 0; + } + + if (!kem->method->encaps_deterministic(ciphertext, shared_secret, key->public_key, seed)) { + return 0; + } + + // The size of the ciphertext and the shared secret + // that has been writen to the output buffers. + *ciphertext_len = kem->ciphertext_len; + *shared_secret_len = kem->shared_secret_len; + + return 1; +} + static int pkey_kem_encapsulate(EVP_PKEY_CTX *ctx, uint8_t *ciphertext, size_t *ciphertext_len, @@ -189,6 +278,8 @@ const EVP_PKEY_METHOD kem_pkey_meth = { NULL, NULL, NULL, + pkey_kem_keygen_deterministic, + pkey_kem_encapsulate_deterministic, pkey_kem_encapsulate, pkey_kem_decapsulate, }; diff --git a/crypto/evp_extra/p_x25519.c b/crypto/evp_extra/p_x25519.c index 65e67be7a83..d591cc7fa2d 100644 --- a/crypto/evp_extra/p_x25519.c +++ b/crypto/evp_extra/p_x25519.c @@ -109,6 +109,8 @@ const EVP_PKEY_METHOD x25519_pkey_meth = { pkey_x25519_derive, NULL /* paramgen */, pkey_x25519_ctrl, + NULL /* keygen deterministic */, + NULL /* encapsulate deterministic */, NULL /* encapsulate */, NULL /* decapsulate */, }; diff --git a/crypto/fipsmodule/evp/evp_ctx.c b/crypto/fipsmodule/evp/evp_ctx.c index 801fe0a12a8..fb88b9e075d 100644 --- a/crypto/fipsmodule/evp/evp_ctx.c +++ b/crypto/fipsmodule/evp/evp_ctx.c @@ -55,6 +55,7 @@ * [including the GNU Public Licence.] */ #include +#include #include @@ -438,6 +439,49 @@ int EVP_PKEY_keygen_init(EVP_PKEY_CTX *ctx) { return 1; } +int EVP_PKEY_keygen_deterministic(EVP_PKEY_CTX *ctx, + EVP_PKEY **out_pkey, + const uint8_t *seed) { + // We have to avoid potential underlying services updating the indicator state, + // so we lock the state here. + FIPS_service_indicator_lock_state(); + int ret = 0; + if (!ctx || !ctx->pmeth || (!ctx->pmeth->keygen && !ctx->pmeth->keygen_deterministic)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + goto end; + } + if (ctx->operation != EVP_PKEY_OP_KEYGEN) { + OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATON_NOT_INITIALIZED); + goto end; + } + + if (!out_pkey) { + goto end; + } + + if (!*out_pkey) { + *out_pkey = EVP_PKEY_new(); + if (!*out_pkey) { + OPENSSL_PUT_ERROR(EVP, ERR_LIB_EVP); + goto end; + } + } + + if (!ctx->pmeth->keygen_deterministic(ctx, *out_pkey, seed)) { + EVP_PKEY_free(*out_pkey); + *out_pkey = NULL; + goto end; + } + + ret = 1; +end: + FIPS_service_indicator_unlock_state(); + if(ret) { + EVP_PKEY_keygen_verify_service_indicator(*out_pkey); + } + return ret; +} + int EVP_PKEY_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey) { // We have to avoid potential underlying services updating the indicator state, // so we lock the state here. @@ -518,6 +562,21 @@ int EVP_PKEY_paramgen(EVP_PKEY_CTX *ctx, EVP_PKEY **out_pkey) { return 1; } +int EVP_PKEY_encapsulate_deterministic(EVP_PKEY_CTX *ctx, + uint8_t *ciphertext, + size_t *ciphertext_len, + uint8_t *shared_secret, + size_t *shared_secret_len, + const uint8_t *seed) { + if (ctx == NULL || ctx->pmeth == NULL || ctx->pmeth->encapsulate_deterministic== NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return 0; + } + + return ctx->pmeth->encapsulate_deterministic(ctx, ciphertext, ciphertext_len, + shared_secret, shared_secret_len, seed); +} + int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx, uint8_t *ciphertext, size_t *ciphertext_len, uint8_t *shared_secret, size_t *shared_secret_len) { diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index 5080be1f4c6..86f8ba166ce 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -279,8 +279,19 @@ struct evp_pkey_method_st { int (*ctrl)(EVP_PKEY_CTX *ctx, int type, int p1, void *p2); - // Encapsulate and decapsulate are operations defined for a - // Key Encapsulation Mechanism (KEM). + // Encapsulate, encapsulate_deterministic, keygen_deterministic, and + // decapsulate are operations defined for a Key Encapsulation Mechanism (KEM). + int (*keygen_deterministic)(EVP_PKEY_CTX *ctx, + EVP_PKEY *pkey, + const uint8_t *seed); + + int (*encapsulate_deterministic)(EVP_PKEY_CTX *ctx, + uint8_t *ciphertext, + size_t *ciphertext_len, + uint8_t *shared_secret, + size_t *shared_secret_len, + const uint8_t *seed); + int (*encapsulate)(EVP_PKEY_CTX *ctx, uint8_t *ciphertext, size_t *ciphertext_len, uint8_t *shared_secret, size_t *shared_secret_len); diff --git a/crypto/kem/internal.h b/crypto/kem/internal.h index 9462fe5e459..6c8301ec53a 100644 --- a/crypto/kem/internal.h +++ b/crypto/kem/internal.h @@ -13,9 +13,18 @@ extern "C" { // KEM_METHOD structure and helper functions. typedef struct { + int (*keygen_deterministic)(uint8_t *ctx, + uint8_t *pkey, + const uint8_t *seed); + int (*keygen)(uint8_t *public_key, uint8_t *secret_key); + int (*encaps_deterministic)(uint8_t *ciphertext, + uint8_t *shared_secret, + const uint8_t *public_key, + const uint8_t *seed); + int (*encaps)(uint8_t *ciphertext, uint8_t *shared_secret, const uint8_t *public_key); diff --git a/crypto/kem/kem_methods.c b/crypto/kem/kem_methods.c index 44d77f9dde1..0312f5333a1 100644 --- a/crypto/kem/kem_methods.c +++ b/crypto/kem/kem_methods.c @@ -28,7 +28,9 @@ static int kyber512r3_decaps(uint8_t *shared_secret, } const KEM_METHOD kem_kyber512r3_method = { + NULL, /* keygen deterministic */ kyber512r3_keygen, + NULL, /* encaps deterministic */ kyber512r3_encaps, kyber512r3_decaps, }; @@ -51,7 +53,9 @@ static int kyber768r3_decaps(uint8_t *shared_secret, } const KEM_METHOD kem_kyber768r3_method = { + NULL, /* keygen deterministic */ kyber768r3_keygen, + NULL, /* encaps deterministic */ kyber768r3_encaps, kyber768r3_decaps, }; @@ -74,16 +78,31 @@ static int kyber1024r3_decaps(uint8_t *shared_secret, } const KEM_METHOD kem_kyber1024r3_method = { + NULL, /* keygen deterministic */ kyber1024r3_keygen, + NULL, /* encaps deterministic */ kyber1024r3_encaps, kyber1024r3_decaps, }; +static int ml_kem_512_ipd_keygen_deterministic(uint8_t *public_key, + uint8_t *secret_key, + const uint8_t *seed) { + return ml_kem_512_ipd_keypair_deterministic(public_key, secret_key, seed) == 0; +} + static int ml_kem_512_ipd_keygen(uint8_t *public_key, uint8_t *secret_key) { return ml_kem_512_ipd_keypair(public_key, secret_key) == 0; } +static int ml_kem_512_ipd_encaps_deterministic(uint8_t *ciphertext, + uint8_t *shared_secret, + const uint8_t *public_key, + const uint8_t *seed) { + return ml_kem_512_ipd_encapsulate_deterministic(ciphertext, shared_secret, public_key, seed) == 0; +} + static int ml_kem_512_ipd_encaps(uint8_t *ciphertext, uint8_t *shared_secret, const uint8_t *public_key) { @@ -97,7 +116,9 @@ static int ml_kem_512_ipd_decaps(uint8_t *shared_secret, } const KEM_METHOD kem_ml_kem_512_ipd_method = { + ml_kem_512_ipd_keygen_deterministic, ml_kem_512_ipd_keygen, + ml_kem_512_ipd_encaps_deterministic, ml_kem_512_ipd_encaps, ml_kem_512_ipd_decaps, }; diff --git a/crypto/ml_kem/ml_kem.c b/crypto/ml_kem/ml_kem.c index 5610ea14779..b4d05a630f0 100644 --- a/crypto/ml_kem/ml_kem.c +++ b/crypto/ml_kem/ml_kem.c @@ -9,11 +9,24 @@ // conditionally (or based on compile-time flags) called here, depending on // platform support. +int ml_kem_512_ipd_keypair_deterministic(uint8_t *public_key /* OUT */, + uint8_t *secret_key /* OUT */, + const uint8_t *seed /* IN */) { + return ml_kem_512_ref_keypair_derand(public_key, secret_key, seed); +} + int ml_kem_512_ipd_keypair(uint8_t *public_key /* OUT */, uint8_t *secret_key /* OUT */) { return ml_kem_512_ref_keypair(public_key, secret_key); } +int ml_kem_512_ipd_encapsulate_deterministic(uint8_t *ciphertext /* OUT */, + uint8_t *shared_secret /* OUT */, + const uint8_t *public_key /* IN */, + const uint8_t *seed /* IN */) { + return ml_kem_512_ref_enc_derand(ciphertext, shared_secret, public_key, seed); +} + int ml_kem_512_ipd_encapsulate(uint8_t *ciphertext /* OUT */, uint8_t *shared_secret /* OUT */, const uint8_t *public_key /* IN */) { diff --git a/crypto/ml_kem/ml_kem.h b/crypto/ml_kem/ml_kem.h index a2206fd8c5b..03b796b5661 100644 --- a/crypto/ml_kem/ml_kem.h +++ b/crypto/ml_kem/ml_kem.h @@ -12,9 +12,18 @@ #define MLKEM512IPD_SECRET_KEY_BYTES (1632) #define MLKEM512IPD_CIPHERTEXT_BYTES (768) +int ml_kem_512_ipd_keypair_deterministic(uint8_t *public_key /* OUT */, + uint8_t *secret_key /* OUT */, + const uint8_t *seed /* IN */); + int ml_kem_512_ipd_keypair(uint8_t *public_key /* OUT */, uint8_t *secret_key /* OUT */); +int ml_kem_512_ipd_encapsulate_deterministic(uint8_t *ciphertext /* OUT */, + uint8_t *shared_secret /* OUT */, + const uint8_t *public_key /* IN */, + const uint8_t *seed /* IN */); + int ml_kem_512_ipd_encapsulate(uint8_t *ciphertext /* OUT */, uint8_t *shared_secret /* OUT */, const uint8_t *public_key /* IN */); diff --git a/include/openssl/experimental/ml-kem.h b/include/openssl/experimental/ml-kem.h new file mode 100644 index 00000000000..88c5c67f3d9 --- /dev/null +++ b/include/openssl/experimental/ml-kem.h @@ -0,0 +1,64 @@ +// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 OR ISC + +// These APIs are marked as experimental as the development and standardization +// of KEMs (e.g., for FIPS 203) are being finalized. + +#include + +#include // IWYU pragma: export +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +// EVP_PKEY_keygen_deterministic is an operation defined for a KEM (Key +// Encapsulation Mechanism). For the KEM specified in |ctx|, the function +// performs deterministic keygen based on the value specified in |seed|. +// +// EVP_PKEY_keygen_deterministic performs a deterministic key generation +// operation using the values from |ctx|, and the given |seed|. If |*out_pkey| +// is non-NULL, it overwrites |*out_pkey| with the resulting key. Otherwise, it +// sets |*out_pkey| to a newly-allocated |EVP_PKEY| containing the result. +// It returns one on success or zero on error. +OPENSSL_EXPORT int EVP_PKEY_keygen_deterministic(EVP_PKEY_CTX *ctx /* IN */, + EVP_PKEY **out_pkey /* OUT */, + const uint8_t *seed /* IN */); + +// EVP_PKEY_encapsulate_deterministic is an operation defined for a KEM (Key +// Encapsulation Mechanism). For the KEM specified in |ctx|, the function: +// 1. generates a deterministic value (derived from |seed|) and writes it to +// |shared_secret|, +// 2. encapsulates the shared secret, producing the ciphertext, by using +// the public key in |ctx|, and writes the ciphertext to |ciphertext|, +// 3. writes the length of |ciphertext| and |shared_secret| to +// |ciphertext_len| and |shared_secret_len|. +// +// The function requires that output buffers, |ciphertext| and |shared_secret|, +// be either both NULL or both non-NULL. Otherwise, a failure is returned. +// +// If both |ciphertext| and |shared_secret| are NULL it is assumed that +// the caller is doing a size check: the function will write the size of +// the ciphertext and the shared secret in |ciphertext_len| and +// |shared_secret_len| and return successfully. +// +// If both |ciphertext| and |shared_secret| are not NULL it is assumed that +// the caller is performing the actual operation. The function will check +// additionally if the lengths of the output buffers, |ciphertext_len| and +// |shared_secret_len|, are large enough for the KEM. +// +// NOTE: no allocation is done in the function, the caller is expected to +// provide large enough |ciphertext| and |shared_secret| buffers. +// +// It returns one on success or zero on error. +OPENSSL_EXPORT int EVP_PKEY_encapsulate_deterministic(EVP_PKEY_CTX *ctx /* IN */, + uint8_t *ciphertext /* OUT */, + size_t *ciphertext_len /* OUT */, + uint8_t *shared_secret /* OUT */, + size_t *shared_secret_len /* OUT */, + const uint8_t *seed /* IN */); + +#if defined(__cplusplus) +} // extern C +#endif