]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add HPKE DHKEM provider support for EC, X25519 and X448.
authorslontis <shane.lontis@oracle.com>
Fri, 26 Aug 2022 01:54:35 +0000 (11:54 +1000)
committerHugo Landau <hlandau@openssl.org>
Fri, 23 Sep 2022 08:24:47 +0000 (09:24 +0100)
The code is derived from @sftcd's work in PR #17172.
This PR puts the DHKEM algorithms into the provider layer as
KEM algorithms for EC and ECX.

This PR only implements the DHKEM component of HPKE as specified in
RFC 9180.

crypto/hpke/hpke_util.c has been added for fuctions that will
be shared between DHKEM and HPKE.

API's for EVP_PKEY_auth_encapsulate_init() and EVP_PKEY_auth_decapsulate_init()
have been added to support authenticated encapsulation. auth_init() functions
were chosen rather that a EVP_PKEY_KEM_set_auth() interface to support
future algorithms that could possibly need different init functions.

Internal code has been refactored, so that it can be shared between the DHKEM
and other systems. Since DHKEM operates on low level keys it needs to be
able to do low level ECDH and ECXDH calls without converting the keys
back into EVP_PKEY/EVP_PKEY_CTX form. See ossl_ecx_compute_key(),
ossl_ec_public_from_private()

DHKEM requires API's to derive a key using a seed (IKM). This did not sit
well inside the DHKEM itself as dispatch functions. This functionality
fits better inside the EC and ECX keymanagers keygen, since
they are just variations of keygen where the private key is generated
in a different manner. This should mainly be used for testing purposes.
See ossl_ec_generate_key_dhkem().
It supports this by allowing a settable param to be passed to keygen
(See OSSL_PKEY_PARAM_DHKEM_IKM).
The keygen calls code within ec and ecx dhkem implementation to handle this.
See ossl_ecx_dhkem_derive_private() and ossl_ec_dhkem_derive_private().
These 2 functions are also used by the EC/ECX DHKEM implementations to generate
the sender ephemeral keys.

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19068)

39 files changed:
crypto/build.info
crypto/ec/build.info
crypto/ec/ec_key.c
crypto/ec/ecx_key.c
crypto/evp/evp_local.h
crypto/evp/kem.c
crypto/hpke/build.info [new file with mode: 0644]
crypto/hpke/hpke_util.c [new file with mode: 0644]
doc/build.info
doc/man3/EVP_PKEY_decapsulate.pod
doc/man3/EVP_PKEY_encapsulate.pod
doc/man7/EVP_KEM-EC.pod [new file with mode: 0644]
doc/man7/EVP_KEM-X25519.pod [new file with mode: 0644]
doc/man7/EVP_PKEY-EC.pod
doc/man7/EVP_PKEY-X25519.pod
doc/man7/OSSL_PROVIDER-default.pod
doc/man7/provider-kem.pod
include/crypto/ec.h
include/crypto/ecx.h
include/crypto/hpke.h [new file with mode: 0644]
include/openssl/core_dispatch.h
include/openssl/core_names.h
include/openssl/evp.h
providers/defltprov.c
providers/implementations/exchange/ecx_exch.c
providers/implementations/include/prov/ecx.h [new file with mode: 0644]
providers/implementations/include/prov/implementations.h
providers/implementations/kem/build.info
providers/implementations/kem/ec_kem.c [new file with mode: 0644]
providers/implementations/kem/eckem.h [new file with mode: 0644]
providers/implementations/kem/ecx_kem.c [new file with mode: 0644]
providers/implementations/kem/kem_util.c [new file with mode: 0644]
providers/implementations/keymgmt/ec_kmgmt.c
providers/implementations/keymgmt/ecx_kmgmt.c
test/build.info
test/dhkem_test.inc [new file with mode: 0644]
test/evp_pkey_dhkem_test.c [new file with mode: 0644]
test/recipes/30-test_evp_pkey_dhkem.t [new file with mode: 0644]
util/libcrypto.num

index 35e012d5d2c3ae004aeafaecf7e7d384d38882f3..1c9ca3a80998ffc6e96dc4aa639a4215df7eff26 100644 (file)
@@ -6,7 +6,7 @@ SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 conf \
         siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \
         seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \
         err comp http ocsp cms ts srp cmac ct async ess crmf cmp encode_decode \
-        ffc
+        ffc hpke
 
 LIBS=../libcrypto
 
index a511e887a9ba19a7765264fa1ee55b80e5cce09a..e4799ad37a24cce12615a4205365d7e72c07e9f4 100644 (file)
@@ -92,6 +92,7 @@ INCLUDE[ecp_nistz256-sparcv9.o]=..
 INCLUDE[ecp_s390x_nistp.o]=..
 INCLUDE[ecx_s390x.o]=..
 INCLUDE[ecx_meth.o]=..
+INCLUDE[ecx_key.o]=..
 
 GENERATE[ecp_nistz256-armv4.S]=asm/ecp_nistz256-armv4.pl
 INCLUDE[ecp_nistz256-armv4.o]=..
index 1bbca360e2f5e3e3fe67e66a3e10bf635b7cf65d..44bac9afa7bbb2e8fae60132f63fcb3807ae48df 100644 (file)
@@ -24,6 +24,7 @@
 #endif
 #include <openssl/self_test.h>
 #include "prov/providercommon.h"
+#include "prov/ecx.h"
 #include "crypto/bn.h"
 
 static int ecdsa_keygen_pairwise_test(EC_KEY *eckey, OSSL_CALLBACK *cb,
@@ -350,6 +351,43 @@ err:
     return ok;
 }
 
+#ifndef FIPS_MODULE
+/*
+ * This is similar to ec_generate_key(), except it uses an ikm to
+ * derive the private key.
+ */
+int ossl_ec_generate_key_dhkem(EC_KEY *eckey,
+                               const unsigned char *ikm, size_t ikmlen)
+{
+    int ok = 0;
+
+    if (eckey->priv_key == NULL) {
+        eckey->priv_key = BN_secure_new();
+        if (eckey->priv_key == NULL)
+            goto err;
+    }
+    if (ossl_ec_dhkem_derive_private(eckey, eckey->priv_key, ikm, ikmlen) <= 0)
+        goto err;
+    if (eckey->pub_key == NULL) {
+        eckey->pub_key = EC_POINT_new(eckey->group);
+        if (eckey->pub_key == NULL)
+            goto err;
+    }
+    if (!ossl_ec_key_simple_generate_public_key(eckey))
+        goto err;
+
+    ok = 1;
+err:
+    if (!ok) {
+        BN_clear_free(eckey->priv_key);
+        eckey->priv_key = NULL;
+        if (eckey->pub_key != NULL)
+            EC_POINT_set_to_infinity(eckey->group, eckey->pub_key);
+    }
+    return ok;
+}
+#endif
+
 int ossl_ec_key_simple_generate_key(EC_KEY *eckey)
 {
     return ec_generate_key(eckey, 0);
index dcec26c2e9b38f4df2438dfc11b073fad147539b..8cf7f1708cc954d9af2063bd9156da1fb025f582 100644 (file)
@@ -9,7 +9,13 @@
 
 #include <string.h>
 #include <openssl/err.h>
+#include <openssl/proverr.h>
 #include "crypto/ecx.h"
+#include "internal/common.h" /* for ossl_assert() */
+
+#ifdef S390X_EC_ASM
+# include "s390x_arch.h"
+#endif
 
 ECX_KEY *ossl_ecx_key_new(OSSL_LIB_CTX *libctx, ECX_KEY_TYPE type, int haspubkey,
                           const char *propq)
@@ -96,3 +102,61 @@ unsigned char *ossl_ecx_key_allocate_privkey(ECX_KEY *key)
 
     return key->privkey;
 }
+
+int ossl_ecx_compute_key(ECX_KEY *peer, ECX_KEY *priv, size_t keylen,
+                         unsigned char *secret, size_t *secretlen, size_t outlen)
+{
+    if (priv == NULL
+            || priv->privkey == NULL
+            || peer == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+        return 0;
+    }
+
+    if (!ossl_assert(keylen == X25519_KEYLEN
+            || keylen == X448_KEYLEN)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+        return 0;
+    }
+
+    if (secret == NULL) {
+        *secretlen = keylen;
+        return 1;
+    }
+    if (outlen < keylen) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+        return 0;
+    }
+
+    if (keylen == X25519_KEYLEN) {
+#ifdef S390X_EC_ASM
+        if (OPENSSL_s390xcap_P.pcc[1]
+                & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X25519)) {
+            if (s390x_x25519_mul(secret, peer->pubkey, priv->privkey) == 0) {
+                ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+                return 0;
+            }
+        } else
+#endif
+        if (ossl_x25519(secret, priv->privkey, peer->pubkey) == 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+            return 0;
+        }
+    } else {
+#ifdef S390X_EC_ASM
+        if (OPENSSL_s390xcap_P.pcc[1]
+                & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X448)) {
+            if (s390x_x448_mul(secret, peer->pubkey, priv->privkey) == 0) {
+                ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+                return 0;
+            }
+        } else
+#endif
+        if (ossl_x448(secret, priv->privkey, peer->pubkey) == 0) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+            return 0;
+        }
+    }
+    *secretlen = keylen;
+    return 1;
+}
index a853174452c7eb2a2634fde680b699922cdf5464..8c26e8fd6d4637d8f963d66c7d31faacff8aae86 100644 (file)
@@ -228,6 +228,8 @@ struct evp_kem_st {
     OSSL_FUNC_kem_gettable_ctx_params_fn *gettable_ctx_params;
     OSSL_FUNC_kem_set_ctx_params_fn *set_ctx_params;
     OSSL_FUNC_kem_settable_ctx_params_fn *settable_ctx_params;
+    OSSL_FUNC_kem_auth_encapsulate_init_fn *auth_encapsulate_init;
+    OSSL_FUNC_kem_auth_decapsulate_init_fn *auth_decapsulate_init;
 } /* EVP_KEM */;
 
 int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass,
index bd28ede7aeb8911a401a0d725bee7fb58cb09969..8c0c35b54b16859a8d84e27ea28ea9fd2e896cac 100644 (file)
 #include "evp_local.h"
 
 static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
-                        const OSSL_PARAM params[])
+                        const OSSL_PARAM params[], EVP_PKEY *authkey)
 {
     int ret = 0;
     EVP_KEM *kem = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
     const OSSL_PROVIDER *tmp_prov = NULL;
-    void *provkey = NULL;
+    void *provkey = NULL, *provauthkey = NULL;
     const char *supported_kem = NULL;
     int iter;
 
@@ -40,7 +40,10 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
         ERR_raise(ERR_LIB_EVP, EVP_R_NO_KEY_SET);
         goto err;
     }
-
+    if (authkey != NULL && authkey->type != ctx->pkey->type) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES);
+        return 0;
+    }
     /*
      * Try to derive the supported kem from |ctx->keymgmt|.
      */
@@ -114,16 +117,26 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
          * same property query as when fetching the kem method.
          * With the keymgmt we found (if we did), we try to export |ctx->pkey|
          * to it (evp_pkey_export_to_provider() is smart enough to only actually
-
          * export it if |tmp_keymgmt| is different from |ctx->pkey|'s keymgmt)
          */
         tmp_keymgmt_tofree = tmp_keymgmt =
             evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
                                         EVP_KEYMGMT_get0_name(ctx->keymgmt),
                                         ctx->propquery);
-        if (tmp_keymgmt != NULL)
+        if (tmp_keymgmt != NULL) {
             provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
                                                   &tmp_keymgmt, ctx->propquery);
+            if (provkey != NULL && authkey != NULL) {
+                provauthkey = evp_pkey_export_to_provider(authkey, ctx->libctx,
+                                                          &tmp_keymgmt,
+                                                          ctx->propquery);
+                if (provauthkey == NULL) {
+                    EVP_KEM_free(kem);
+                    ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+                    goto err;
+                }
+            }
+        }
         if (tmp_keymgmt == NULL)
             EVP_KEYMGMT_free(tmp_keymgmt_tofree);
     }
@@ -144,20 +157,28 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
 
     switch (operation) {
     case EVP_PKEY_OP_ENCAPSULATE:
-        if (kem->encapsulate_init == NULL) {
+        if (provauthkey != NULL && kem->auth_encapsulate_init != NULL) {
+            ret = kem->auth_encapsulate_init(ctx->op.encap.algctx, provkey,
+                                             provauthkey, params);
+        } else if (provauthkey == NULL && kem->encapsulate_init != NULL) {
+            ret = kem->encapsulate_init(ctx->op.encap.algctx, provkey, params);
+        } else {
             ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
             ret = -2;
             goto err;
         }
-        ret = kem->encapsulate_init(ctx->op.encap.algctx, provkey, params);
         break;
     case EVP_PKEY_OP_DECAPSULATE:
-        if (kem->decapsulate_init == NULL) {
+        if (provauthkey != NULL && kem->auth_decapsulate_init != NULL) {
+            ret = kem->auth_decapsulate_init(ctx->op.encap.algctx, provkey,
+                                             provauthkey, params);
+        } else if (provauthkey == NULL && kem->encapsulate_init != NULL) {
+            ret = kem->decapsulate_init(ctx->op.encap.algctx, provkey, params);
+        } else {
             ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
             ret = -2;
             goto err;
         }
-        ret = kem->decapsulate_init(ctx->op.encap.algctx, provkey, params);
         break;
     default:
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
@@ -178,9 +199,17 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
     return ret;
 }
 
+int EVP_PKEY_auth_encapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpriv,
+                                   const OSSL_PARAM params[])
+{
+    if (authpriv == NULL)
+        return 0;
+    return evp_kem_init(ctx, EVP_PKEY_OP_ENCAPSULATE, params, authpriv);
+}
+
 int EVP_PKEY_encapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
 {
-    return evp_kem_init(ctx, EVP_PKEY_OP_ENCAPSULATE, params);
+    return evp_kem_init(ctx, EVP_PKEY_OP_ENCAPSULATE, params, NULL);
 }
 
 int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
@@ -209,7 +238,15 @@ int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
 
 int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
 {
-    return evp_kem_init(ctx, EVP_PKEY_OP_DECAPSULATE, params);
+    return evp_kem_init(ctx, EVP_PKEY_OP_DECAPSULATE, params, NULL);
+}
+
+int EVP_PKEY_auth_decapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpub,
+                                   const OSSL_PARAM params[])
+{
+    if (authpub == NULL)
+        return 0;
+    return evp_kem_init(ctx, EVP_PKEY_OP_DECAPSULATE, params, authpub);
 }
 
 int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx,
@@ -288,6 +325,12 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
             kem->encapsulate_init = OSSL_FUNC_kem_encapsulate_init(fns);
             encfncnt++;
             break;
+        case OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT:
+            if (kem->auth_encapsulate_init != NULL)
+                break;
+            kem->auth_encapsulate_init = OSSL_FUNC_kem_auth_encapsulate_init(fns);
+            encfncnt++;
+            break;
         case OSSL_FUNC_KEM_ENCAPSULATE:
             if (kem->encapsulate != NULL)
                 break;
@@ -300,6 +343,12 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
             kem->decapsulate_init = OSSL_FUNC_kem_decapsulate_init(fns);
             decfncnt++;
             break;
+        case OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT:
+            if (kem->auth_decapsulate_init != NULL)
+                break;
+            kem->auth_decapsulate_init = OSSL_FUNC_kem_auth_decapsulate_init(fns);
+            decfncnt++;
+            break;
         case OSSL_FUNC_KEM_DECAPSULATE:
             if (kem->decapsulate != NULL)
                 break;
@@ -348,19 +397,21 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
         }
     }
     if (ctxfncnt != 2
-        || (encfncnt != 0 && encfncnt != 2)
-        || (decfncnt != 0 && decfncnt != 2)
-        || (encfncnt != 2 && decfncnt != 2)
+        || (encfncnt != 0 && encfncnt != 2 && encfncnt != 3)
+        || (decfncnt != 0 && decfncnt != 2 && decfncnt != 3)
+        || (encfncnt != decfncnt)
         || (gparamfncnt != 0 && gparamfncnt != 2)
         || (sparamfncnt != 0 && sparamfncnt != 2)) {
         /*
          * In order to be a consistent set of functions we must have at least
-         * a set of context functions (newctx and freectx) as well as a pair of
-         * "kem" functions: (encapsulate_init, encapsulate) or
-         * (decapsulate_init, decapsulate). set_ctx_params and settable_ctx_params are
-         * optional, but if one of them is present then the other one must also
-         * be present. The same applies to get_ctx_params and
-         * gettable_ctx_params. The dupctx function is optional.
+         * a set of context functions (newctx and freectx) as well as a pair
+         * (or triplet) of "kem" functions:
+         * (encapsulate_init, (and/or auth_encapsulate_init), encapsulate) or
+         * (decapsulate_init, (and/or auth_decapsulate_init), decapsulate).
+         * set_ctx_params and settable_ctx_params are optional, but if one of
+         * them is present then the other one must also be present. The same
+         * applies to get_ctx_params and gettable_ctx_params.
+         * The dupctx function is optional.
          */
         ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS);
         goto err;
diff --git a/crypto/hpke/build.info b/crypto/hpke/build.info
new file mode 100644 (file)
index 0000000..f096cf1
--- /dev/null
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+
+$COMMON=hpke_util.c
+
+SOURCE[../../libcrypto]=$COMMON
diff --git a/crypto/hpke/hpke_util.c b/crypto/hpke/hpke_util.c
new file mode 100644 (file)
index 0000000..92f9892
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "crypto/hpke.h"
+#include "internal/packet.h"
+
+/*
+ * The largest value happens inside dhkem_extract_and_expand
+ * Which consists of a max dkmlen of 2*privkeylen + suiteid + small label
+ */
+#define LABELED_EXTRACT_SIZE (10 + 12 + 2 * OSSL_HPKE_MAX_PRIVATE)
+
+/*
+ * The largest value happens inside dhkem_extract_and_expand
+ * Which consists of a prklen of secretlen + contextlen of 3 encoded public keys
+ * + suiteid + small label
+ */
+#define LABELED_EXPAND_SIZE (LABELED_EXTRACT_SIZE + 3 * OSSL_HPKE_MAX_PUBLIC)
+
+/* ASCII: "HPKE-v1", in hex for EBCDIC compatibility */
+static const char LABEL_HPKEV1[] = "\x48\x50\x4B\x45\x2D\x76\x31";
+
+static int kdf_derive(EVP_KDF_CTX *kctx,
+                      unsigned char *out, size_t outlen, int mode,
+                      const unsigned char *salt, size_t saltlen,
+                      const unsigned char *ikm, size_t ikmlen,
+                      const unsigned char *info, size_t infolen)
+{
+    int ret;
+    OSSL_PARAM params[5], *p = params;
+
+    *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
+    if (salt != NULL)
+        *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
+                                                 (char *)salt, saltlen);
+    if (ikm != NULL)
+        *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+                                                 (char *)ikm, ikmlen);
+    if (info != NULL)
+        *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+                                                 (char *)info, infolen);
+    *p = OSSL_PARAM_construct_end();
+    ret = EVP_KDF_derive(kctx, out, outlen, params) > 0;
+    if (!ret)
+        ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+    return ret;
+}
+
+int ossl_hpke_kdf_extract(EVP_KDF_CTX *kctx,
+                          unsigned char *prk, size_t prklen,
+                          const unsigned char *salt, size_t saltlen,
+                          const unsigned char *ikm, size_t ikmlen)
+{
+    return kdf_derive(kctx, prk, prklen, EVP_KDF_HKDF_MODE_EXTRACT_ONLY,
+                      salt, saltlen, ikm, ikmlen, NULL, 0);
+}
+
+/* Common code to perform a HKDF expand */
+int ossl_hpke_kdf_expand(EVP_KDF_CTX *kctx,
+                         unsigned char *okm, size_t okmlen,
+                         const unsigned char *prk, size_t prklen,
+                         const unsigned char *info, size_t infolen)
+{
+    return kdf_derive(kctx, okm, okmlen, EVP_KDF_HKDF_MODE_EXPAND_ONLY,
+                      NULL, 0, prk, prklen, info, infolen);
+}
+
+/*
+ * See RFC 9180 Section 4 LabelExtract()
+ */
+int ossl_hpke_labeled_extract(EVP_KDF_CTX *kctx,
+                              unsigned char *prk, size_t prklen,
+                              const unsigned char *salt, size_t saltlen,
+                              const unsigned char *suiteid, size_t suiteidlen,
+                              const char *label,
+                              const unsigned char *ikm, size_t ikmlen)
+{
+    int ret = 0;
+    size_t labeled_ikmlen = 0;
+    unsigned char labeled_ikm[LABELED_EXTRACT_SIZE];
+    WPACKET pkt;
+
+    /* labeled_ikm = concat("HPKE-v1", suiteid, label, ikm) */
+    if (!WPACKET_init_static_len(&pkt, labeled_ikm, sizeof(labeled_ikm), 0)
+            || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, strlen(LABEL_HPKEV1))
+            || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
+            || !WPACKET_memcpy(&pkt, label, strlen(label))
+            || !WPACKET_memcpy(&pkt, ikm, ikmlen)
+            || !WPACKET_get_total_written(&pkt, &labeled_ikmlen)
+            || !WPACKET_finish(&pkt)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+        goto end;
+    }
+
+    ret = ossl_hpke_kdf_extract(kctx, prk, prklen, salt, saltlen,
+                                labeled_ikm, labeled_ikmlen);
+end:
+    WPACKET_cleanup(&pkt);
+    OPENSSL_cleanse(labeled_ikm, labeled_ikmlen);
+    return ret;
+}
+
+/*
+ * See RFC 9180 Section 4 LabelExpand()
+ */
+int ossl_hpke_labeled_expand(EVP_KDF_CTX *kctx,
+                             unsigned char *okm, size_t okmlen,
+                             const unsigned char *prk, size_t prklen,
+                             const unsigned char *suiteid, size_t suiteidlen,
+                             const char *label,
+                             const unsigned char *info, size_t infolen)
+{
+    int ret = 0;
+    size_t labeled_infolen = 0;
+    unsigned char labeled_info[LABELED_EXPAND_SIZE];
+    WPACKET pkt;
+
+    /* labeled_info = concat(okmlen, "HPKE-v1", suiteid, label, info) */
+    if (!WPACKET_init_static_len(&pkt, labeled_info, sizeof(labeled_info), 0)
+            || !WPACKET_put_bytes_u16(&pkt, okmlen)
+            || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, strlen(LABEL_HPKEV1))
+            || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
+            || !WPACKET_memcpy(&pkt, label, strlen(label))
+            || !WPACKET_memcpy(&pkt, info, infolen)
+            || !WPACKET_get_total_written(&pkt, &labeled_infolen)
+            || !WPACKET_finish(&pkt)) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+        goto end;
+    }
+
+    ret = ossl_hpke_kdf_expand(kctx, okm, okmlen,
+                               prk, prklen, labeled_info, labeled_infolen);
+end:
+    WPACKET_cleanup(&pkt);
+    return ret;
+}
+
+/* Common code to create a HKDF ctx */
+EVP_KDF_CTX *ossl_kdf_ctx_create(const char *kdfname, const char *mdname,
+                                 OSSL_LIB_CTX *libctx, const char *propq)
+{
+    EVP_KDF *kdf;
+    EVP_KDF_CTX *kctx = NULL;
+
+    kdf = EVP_KDF_fetch(libctx, kdfname, propq);
+    kctx = EVP_KDF_CTX_new(kdf);
+    EVP_KDF_free(kdf);
+    if (kctx != NULL && mdname != NULL) {
+        OSSL_PARAM params[3], *p = params;
+
+        if (mdname != NULL)
+            *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+                                                    (char *)mdname, 0);
+        if (propq != NULL)
+            *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_PROPERTIES,
+                                                    (char *)propq, 0);
+        *p = OSSL_PARAM_construct_end();
+        if (EVP_KDF_CTX_set_params(kctx, params) <= 0) {
+            EVP_KDF_CTX_free(kctx);
+            return NULL;
+        }
+    }
+    return kctx;
+}
index b90ad11eae1bef8964d4b71ec7282cd1096f5ab3..8b55bcdbc7d80af8b3f297b7628be978f05fe213 100644 (file)
@@ -4209,10 +4209,18 @@ DEPEND[html/man7/EVP_KDF-X963.html]=man7/EVP_KDF-X963.pod
 GENERATE[html/man7/EVP_KDF-X963.html]=man7/EVP_KDF-X963.pod
 DEPEND[man/man7/EVP_KDF-X963.7]=man7/EVP_KDF-X963.pod
 GENERATE[man/man7/EVP_KDF-X963.7]=man7/EVP_KDF-X963.pod
+DEPEND[html/man7/EVP_KEM-EC.html]=man7/EVP_KEM-EC.pod
+GENERATE[html/man7/EVP_KEM-EC.html]=man7/EVP_KEM-EC.pod
+DEPEND[man/man7/EVP_KEM-EC.7]=man7/EVP_KEM-EC.pod
+GENERATE[man/man7/EVP_KEM-EC.7]=man7/EVP_KEM-EC.pod
 DEPEND[html/man7/EVP_KEM-RSA.html]=man7/EVP_KEM-RSA.pod
 GENERATE[html/man7/EVP_KEM-RSA.html]=man7/EVP_KEM-RSA.pod
 DEPEND[man/man7/EVP_KEM-RSA.7]=man7/EVP_KEM-RSA.pod
 GENERATE[man/man7/EVP_KEM-RSA.7]=man7/EVP_KEM-RSA.pod
+DEPEND[html/man7/EVP_KEM-X25519.html]=man7/EVP_KEM-X25519.pod
+GENERATE[html/man7/EVP_KEM-X25519.html]=man7/EVP_KEM-X25519.pod
+DEPEND[man/man7/EVP_KEM-X25519.7]=man7/EVP_KEM-X25519.pod
+GENERATE[man/man7/EVP_KEM-X25519.7]=man7/EVP_KEM-X25519.pod
 DEPEND[html/man7/EVP_KEYEXCH-DH.html]=man7/EVP_KEYEXCH-DH.pod
 GENERATE[html/man7/EVP_KEYEXCH-DH.html]=man7/EVP_KEYEXCH-DH.pod
 DEPEND[man/man7/EVP_KEYEXCH-DH.7]=man7/EVP_KEYEXCH-DH.pod
@@ -4630,7 +4638,9 @@ html/man7/EVP_KDF-TLS1_PRF.html \
 html/man7/EVP_KDF-X942-ASN1.html \
 html/man7/EVP_KDF-X942-CONCAT.html \
 html/man7/EVP_KDF-X963.html \
+html/man7/EVP_KEM-EC.html \
 html/man7/EVP_KEM-RSA.html \
+html/man7/EVP_KEM-X25519.html \
 html/man7/EVP_KEYEXCH-DH.html \
 html/man7/EVP_KEYEXCH-ECDH.html \
 html/man7/EVP_KEYEXCH-X25519.html \
@@ -4755,7 +4765,9 @@ man/man7/EVP_KDF-TLS1_PRF.7 \
 man/man7/EVP_KDF-X942-ASN1.7 \
 man/man7/EVP_KDF-X942-CONCAT.7 \
 man/man7/EVP_KDF-X963.7 \
+man/man7/EVP_KEM-EC.7 \
 man/man7/EVP_KEM-RSA.7 \
+man/man7/EVP_KEM-X25519.7 \
 man/man7/EVP_KEYEXCH-DH.7 \
 man/man7/EVP_KEYEXCH-ECDH.7 \
 man/man7/EVP_KEYEXCH-X25519.7 \
index 529e318f9eba013f2a81ccc779d5a68c7ce95fc8..cdda54d12faa6bea44f3e12181821ea8be8707f9 100644 (file)
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-EVP_PKEY_decapsulate_init, EVP_PKEY_decapsulate
+EVP_PKEY_decapsulate_init, EVP_PKEY_auth_decapsulate_init, EVP_PKEY_decapsulate
 - Key decapsulation using a private key algorithm
 
 =head1 SYNOPSIS
@@ -10,6 +10,8 @@ EVP_PKEY_decapsulate_init, EVP_PKEY_decapsulate
  #include <openssl/evp.h>
 
  int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[]);
+ int EVP_PKEY_auth_decapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpub,
+                                   const OSSL_PARAM params[]);
  int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx,
                           unsigned char *secret, size_t *secretlen,
                           const unsigned char *wrapped, size_t wrappedlen);
@@ -20,6 +22,10 @@ The EVP_PKEY_decapsulate_init() function initializes a private key algorithm
 context I<ctx> for a decapsulation operation and then sets the I<params>
 on the context in the same way as calling L<EVP_PKEY_CTX_set_params(3)>.
 
+The EVP_PKEY_auth_decapsulate_init() function is similiar to
+EVP_PKEY_decapsulate_init() but also passes an I<authpub> authentication public
+key that is used during decapsulation.
+
 The EVP_PKEY_decapsulate() function performs a private key decapsulation
 operation using I<ctx>. The data to be decapsulated is specified using the
 I<wrapped> and I<wrappedlen> parameters.
@@ -35,9 +41,10 @@ for the operation may be set or modified using L<EVP_PKEY_CTX_set_params(3)>.
 
 =head1 RETURN VALUES
 
-EVP_PKEY_decapsulate_init() and EVP_PKEY_decapsulate() return 1 for
-success and 0 or a negative value for failure. In particular a return value of -2
-indicates the operation is not supported by the private key algorithm.
+EVP_PKEY_decapsulate_init(), EVP_PKEY_auth_decapsulate_init() and
+EVP_PKEY_decapsulate() return 1 for success and 0 or a negative value for
+failure. In particular a return value of -2 indicates the operation is not
+supported by the private key algorithm.
 
 =head1 EXAMPLES
 
@@ -73,7 +80,7 @@ Decapsulate data using RSA:
      /* malloc failure */
 
  /* Decapsulated secret data is secretlen bytes long */
- if (EVP_PKEY_decapsulaterctx, secret, &secretlen, in, inlen) <= 0)
+ if (EVP_PKEY_decapsulate(ctx, secret, &secretlen, in, inlen) <= 0)
      /* Error */
 
 
@@ -81,15 +88,18 @@ Decapsulate data using RSA:
 
 L<EVP_PKEY_CTX_new(3)>,
 L<EVP_PKEY_encapsulate(3)>,
-L<EVP_KEM-RSA(7)>,
+L<EVP_KEM-RSA(7)>, L<EVP_KEM-X25519(7)>, L<EVP_KEM-EC(7)>
 
 =head1 HISTORY
 
-These functions were added in OpenSSL 3.0.
+The functions EVP_PKEY_decapsulate_init() and EVP_PKEY_decapsulate() were added
+in OpenSSL 3.0.
+
+The function EVP_PKEY_auth_decapsulate_init() was added in OpenSSL 3.1.
 
 =head1 COPYRIGHT
 
-Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
index 9baf88d07beffcc59025076e06562561585ce4ae..262481f2cec0a2f35ab4762a8a01a2632778a807 100644 (file)
@@ -2,7 +2,7 @@
 
 =head1 NAME
 
-EVP_PKEY_encapsulate_init, EVP_PKEY_encapsulate
+EVP_PKEY_encapsulate_init, EVP_PKEY_auth_encapsulate_init, EVP_PKEY_encapsulate
 - Key encapsulation using a public key algorithm
 
 =head1 SYNOPSIS
@@ -10,6 +10,8 @@ EVP_PKEY_encapsulate_init, EVP_PKEY_encapsulate
  #include <openssl/evp.h>
 
  int EVP_PKEY_encapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[]);
+ int EVP_PKEY_auth_encapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpriv,
+                                   const OSSL_PARAM params[]);
  int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
                           unsigned char *out, size_t *outlen,
                           unsigned char *genkey, size_t *genkeylen);
@@ -20,6 +22,10 @@ The EVP_PKEY_encapsulate_init() function initializes a public key algorithm
 context I<ctx> for an encapsulation operation and then sets the I<params>
 on the context in the same way as calling L<EVP_PKEY_CTX_set_params(3)>.
 
+The EVP_PKEY_auth_encapsulate_init() function is similiar to
+EVP_PKEY_encapsulate_init() but also passes an I<authpriv> authentication private
+key that is used during encapsulation.
+
 The EVP_PKEY_encapsulate() function performs a public key encapsulation
 operation using I<ctx> with the name I<name>.
 If I<out> is B<NULL> then the maximum size of the output buffer is written to the
@@ -36,9 +42,10 @@ for the operation may be set or modified using L<EVP_PKEY_CTX_set_params(3)>.
 
 =head1 RETURN VALUES
 
-EVP_PKEY_encapsulate_init() and EVP_PKEY_encapsulate() return 1 for
-success and 0 or a negative value for failure. In particular a return value of -2
-indicates the operation is not supported by the public key algorithm.
+EVP_PKEY_encapsulate_init(), EVP_PKEY_auth_encapsulate_init() and
+EVP_PKEY_encapsulate() return 1 for success and 0 or a negative value for
+failure. In particular a return value of -2 indicates the operation is not
+supported by the public key algorithm.
 
 =head1 EXAMPLES
 
@@ -84,15 +91,17 @@ Encapsulate an RSASVE key (for RSA keys).
 
 L<EVP_PKEY_CTX_new(3)>,
 L<EVP_PKEY_decapsulate(3)>,
-L<EVP_KEM-RSA(7)>,
+L<EVP_KEM-RSA(7)>, L<EVP_KEM-X25519(7)>, L<EVP_KEM-EC(7)>
 
 =head1 HISTORY
 
-These functions were added in OpenSSL 3.0.
+These functions EVP_PKEY_encapsulate_init() and EVP_PKEY_encapsulate() were
+added in OpenSSL 3.0.
+The function EVP_PKEY_auth_encapsulate_init() was added in OpenSSL 3.1.
 
 =head1 COPYRIGHT
 
-Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
diff --git a/doc/man7/EVP_KEM-EC.pod b/doc/man7/EVP_KEM-EC.pod
new file mode 100644 (file)
index 0000000..6e88d13
--- /dev/null
@@ -0,0 +1,78 @@
+=pod
+
+=head1 NAME
+
+EVP_KEM-EC
+- EVP_KEM EC keytype and algorithm support
+
+=head1 DESCRIPTION
+
+The B<EC> keytype and its parameters are described in L<EVP_PKEY-EC(7)>.
+See L<EVP_PKEY_encapsulate(3)> and L<EVP_PKEY_decapsulate(3)> for more info.
+
+=head2 EC KEM parameters
+
+=over 4
+
+=item "operation" (B<OSSL_KEM_PARAM_OPERATION>)<UTF8 string>
+
+The OpenSSL EC Key Encapsulation Mechanisms only supports the
+following operation:
+
+=over 4
+
+=item "DHKEM" (B<OSSL_KEM_PARAM_OPERATION_DHKEM>)
+
+The encapsulate function generates an ephemeral keypair. It produces keymaterial
+by doing an ECDH key exchange using the ephemeral private key and a supplied
+recipient public key. A HKDF operation using the keymaterial and a kem context
+then produces a shared secret. The shared secret and the ephemeral public key
+are returned.
+The decapsulate function uses the recipient private key and the
+ephemeral public key to produce the same keymaterial, which can then be used to
+produce the same shared secret.
+See L<https://www.rfc-editor.org/rfc/rfc9180.html#name-dh-based-kem-dhkem>
+
+=back
+
+This can be set using either EVP_PKEY_CTX_set_kem_op() or
+EVP_PKEY_CTX_set_params().
+
+=item "ikme" (B<OSSL_KEM_PARAM_IKME>) <octet string>
+
+Used to specify the key material used for generation of the ephemeral key.
+This value should not be reused for other purposes.
+It can only be used for the curves "P-256", "P-384" and "P-521" and should
+have a length of at least the size of the encoded private key
+(i.e. 32, 48 and 66 for the listed curves).
+If this value is not set, then a random ikm is used.
+
+=back
+
+=head1 CONFORMING TO
+
+=over 4
+
+=item RFC9180
+
+=back
+
+=head1 SEE ALSO
+
+L<EVP_PKEY_CTX_set_kem_op(3)>,
+L<EVP_PKEY_encapsulate(3)>,
+L<EVP_PKEY_decapsulate(3)>
+L<EVP_KEYMGMT(3)>,
+L<EVP_PKEY(3)>,
+L<provider-keymgmt(7)>
+
+=head1 COPYRIGHT
+
+Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man7/EVP_KEM-X25519.pod b/doc/man7/EVP_KEM-X25519.pod
new file mode 100644 (file)
index 0000000..faf464e
--- /dev/null
@@ -0,0 +1,77 @@
+=pod
+
+=head1 NAME
+
+EVP_KEM-X25519, EVP_KEM-X448
+- EVP_KEM X25519 and EVP_KEM X448 keytype and algorithm support
+
+=head1 DESCRIPTION
+
+The B<X25519> and <X448> keytype and its parameters are described in
+L<EVP_PKEY-X25519(7)>.
+See L<EVP_PKEY_encapsulate(3)> and L<EVP_PKEY_decapsulate(3)> for more info.
+
+=head2 X25519 and X448 KEM parameters
+
+=over 4
+
+=item "operation" (B<OSSL_KEM_PARAM_OPERATION>)<UTF8 string>
+
+The OpenSSL X25519 and X448 Key Encapsulation Mechanisms only support the
+following operation:
+
+=over 4
+
+=item "DHKEM" (B<OSSL_KEM_PARAM_OPERATION_DHKEM>)
+
+The encapsulate function generates an ephemeral keypair. It produces keymaterial
+by doing an X25519 or X448 key exchange using the ephemeral private key and a
+supplied recipient public key. A HKDF operation using the keymaterial and a kem
+context then produces a shared secret. The shared secret and the ephemeral
+public key are returned.
+The decapsulate function uses the recipient private key and the
+ephemeral public key to produce the same keymaterial, which can then be used to
+produce the same shared secret.
+See L<https://www.rfc-editor.org/rfc/rfc9180.html#name-dh-based-kem-dhkem>
+
+=back
+
+This can be set using either EVP_PKEY_CTX_set_kem_op() or
+EVP_PKEY_CTX_set_params().
+
+=item "ikme" (B<OSSL_KEM_PARAM_IKME>) <octet string>
+
+Used to specify the key material used for generation of the ephemeral key.
+This value should not be reused for other purposes.
+It should have a length of at least 32 for X25519, and 56 for X448.
+If this value is not set, then a random ikm is used.
+
+=back
+
+=head1 CONFORMING TO
+
+=over 4
+
+=item RFC9180
+
+=back
+
+=head1 SEE ALSO
+
+L<EVP_PKEY_CTX_set_kem_op(3)>,
+L<EVP_PKEY_encapsulate(3)>,
+L<EVP_PKEY_decapsulate(3)>
+L<EVP_KEYMGMT(3)>,
+L<EVP_PKEY(3)>,
+L<provider-keymgmt(7)>
+
+=head1 COPYRIGHT
+
+Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+
+Licensed under the Apache License 2.0 (the "License").  You may not use
+this file except in compliance with the License.  You can obtain a copy
+in the file LICENSE in the source distribution or at
+L<https://www.openssl.org/source/license.html>.
+
+=cut
index d4c8d9e36e4db2331e1d880583107773632c65d9..0d32c67b438712e38e17f382938fec5876b322e0 100644 (file)
@@ -137,6 +137,14 @@ Used for getting the EC public key Y component.
 Getter that returns the default digest name.
 (Currently returns "SHA256" as of OpenSSL 3.0).
 
+=item "dhkem-ikm" (B<OSSL_PKEY_PARAM_DHKEM_IKM>) <octet string>
+
+DHKEM requires the generation of a keypair using an input key material (seed).
+Use this to specify the key material used for generation of the private key.
+This value should not be reused for other purposes. It can only be used
+for the curves "P-256", "P-384" and "P-521" and should have a length of at least
+the size of the encoded private key (i.e. 32, 48 and 66 for the listed curves).
+
 =back
 
 The following Gettable types are also available for the built-in EC algorithm:
index 9e13e15f7f4fbabd9747db6622d75a0d28cff125..a63e50baa8322164c1c51760ef3fb3bd7d2bd081 100644 (file)
@@ -13,8 +13,22 @@ implemented in OpenSSL's default and FIPS providers.  These implementations
 support the associated key, containing the public key I<pub> and the
 private key I<priv>.
 
-No additional parameters can be set during key generation.
+=head2 Keygen Parameters
 
+=over 4
+
+=item "dhkem-ikm" (B<OSSL_PKEY_PARAM_DHKEM_IKM>) <octet string>
+
+DHKEM requires the generation of a keypair using an input key material (seed).
+Use this to specify the key material used for generation of the private key.
+This value should not be reused for other purposes.
+It should have a length of at least 32 for X25519, and 56 for X448.
+
+This is only supported by X25519 and X448.
+
+=back
+
+Use EVP_PKEY_CTX_set_params() after calling EVP_PKEY_keygen_init().
 
 =head2 Common X25519, X448, ED25519 and ED448 parameters
 
index 7126f9ca3786a9141d5f2578641a8423f7e58ae7..43dc8c330281961d9853d6d999da65caf5c79084 100644 (file)
@@ -194,6 +194,10 @@ The OpenSSL default provider supports these operations and algorithms:
 
 =item RSA, see L<EVP_KEM-RSA(7)>
 
+=item X25519, see L<EVP_KEM-X25519(7)>
+
+=item EC, see L<EVP_KEM-EC(7)>
+
 =back
 
 =head2 Asymmetric Key Management
index f7476e5e9d6159a848ed32b17a50ef0c8e2b5eb5..4e80e5beb78b31d8fd0dca4fa900639d78641e69 100644 (file)
@@ -23,13 +23,19 @@ provider-kem - The kem library E<lt>-E<gt> provider functions
  void *OSSL_FUNC_kem_dupctx(void *ctx);
 
  /* Encapsulation */
- int OSSL_FUNC_kem_encapsulate_init(void *ctx, void *provkey, const char *name,
+ int OSSL_FUNC_kem_encapsulate_init(void *ctx, void *provkey,
                                     const OSSL_PARAM params[]);
+ int OSSL_FUNC_kem_auth_encapsulate_init(void *ctx, void *provkey,
+                                         void *provauthkey,
+                                         const OSSL_PARAM params[]);
  int OSSL_FUNC_kem_encapsulate(void *ctx, unsigned char *out, size_t *outlen,
                                unsigned char *secret, size_t *secretlen);
 
  /* Decapsulation */
- int OSSL_FUNC_kem_decapsulate_init(void *ctx, void *provkey, const char *name);
+ int OSSL_FUNC_kem_decapsulate_init(void *ctx, void *provkey);
+ int OSSL_FUNC_kem_auth_decapsulate_init(void *ctx, void *provkey,
+                                         void *provauthkey,
+                                         const OSSL_PARAM params[]);
  int OSSL_FUNC_kem_decapsulate(void *ctx, unsigned char *out, size_t *outlen,
                                const unsigned char *in, size_t inlen);
 
@@ -68,20 +74,22 @@ For example, the "function" OSSL_FUNC_kem_newctx() has these:
 B<OSSL_DISPATCH> arrays are indexed by numbers that are provided as
 macros in L<openssl-core_dispatch.h(7)>, as follows:
 
- OSSL_FUNC_kem_newctx               OSSL_FUNC_KEM_NEWCTX
- OSSL_FUNC_kem_freectx              OSSL_FUNC_KEM_FREECTX
- OSSL_FUNC_kem_dupctx               OSSL_FUNC_KEM_DUPCTX
+ OSSL_FUNC_kem_newctx                OSSL_FUNC_KEM_NEWCTX
+ OSSL_FUNC_kem_freectx               OSSL_FUNC_KEM_FREECTX
+ OSSL_FUNC_kem_dupctx                OSSL_FUNC_KEM_DUPCTX
 
- OSSL_FUNC_kem_encapsulate_init     OSSL_FUNC_KEM_ENCAPSULATE_INIT
- OSSL_FUNC_kem_encapsulate          OSSL_FUNC_KEM_ENCAPSULATE
+ OSSL_FUNC_kem_encapsulate_init      OSSL_FUNC_KEM_ENCAPSULATE_INIT
+ OSSL_FUNC_kem_auth_encapsulate_init OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT
+ OSSL_FUNC_kem_encapsulate           OSSL_FUNC_KEM_ENCAPSULATE
 
- OSSL_FUNC_kem_decapsulate_init     OSSL_FUNC_KEM_DECAPSULATE_INIT
- OSSL_FUNC_kem_decapsulate          OSSL_FUNC_KEM_DECAPSULATE
+ OSSL_FUNC_kem_decapsulate_init      OSSL_FUNC_KEM_DECAPSULATE_INIT
+ OSSL_FUNC_kem_auth_decapsulate_init OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT
+ OSSL_FUNC_kem_decapsulate           OSSL_FUNC_KEM_DECAPSULATE
 
- OSSL_FUNC_kem_get_ctx_params       OSSL_FUNC_KEM_GET_CTX_PARAMS
- OSSL_FUNC_kem_gettable_ctx_params  OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS
- OSSL_FUNC_kem_set_ctx_params       OSSL_FUNC_KEM_SET_CTX_PARAMS
- OSSL_FUNC_kem_settable_ctx_params  OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS
+ OSSL_FUNC_kem_get_ctx_params        OSSL_FUNC_KEM_GET_CTX_PARAMS
+ OSSL_FUNC_kem_gettable_ctx_params   OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS
+ OSSL_FUNC_kem_set_ctx_params        OSSL_FUNC_KEM_SET_CTX_PARAMS
+ OSSL_FUNC_kem_settable_ctx_params   OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS
 
 An asymmetric kem algorithm implementation may not implement all of these
 functions.
@@ -90,10 +98,12 @@ OSSL_FUNC_kem_newctx and OSSL_FUNC_kem_freectx.
 It must also implement both of OSSL_FUNC_kem_encapsulate_init and
 OSSL_FUNC_kem_encapsulate, or both of OSSL_FUNC_kem_decapsulate_init and
 OSSL_FUNC_kem_decapsulate.
+OSSL_FUNC_kem_auth_encapsulate_init is optional but if it is present then so
+must OSSL_FUNC_kem_auth_decapsulate_init.
 OSSL_FUNC_kem_get_ctx_params is optional but if it is present then so must
 OSSL_FUNC_kem_gettable_ctx_params.
 Similarly, OSSL_FUNC_kem_set_ctx_params is optional but if it is present then
-so must OSSL_FUNC_kem_settable_ctx_params.
+OSSL_FUNC_kem_settable_ctx_params must also be present.
 
 An asymmetric kem algorithm must also implement some mechanism for generating,
 loading or importing keys via the key management (OSSL_OP_KEYMGMT) operation.
@@ -127,6 +137,10 @@ The key object should have been previously generated, loaded or imported into
 the provider using the key management (OSSL_OP_KEYMGMT) operation (see
 provider-keymgmt(7)>.
 
+OSSL_FUNC_kem_auth_encapsulate_init() is similiar to
+OSSL_FUNC_kem_encapsulate_init(), but also passes an additional authentication
+key I<provauthkey> which cannot be NULL.
+
 OSSL_FUNC_kem_encapsulate() performs the actual encapsulation itself.
 A previously initialised asymmetric kem context is passed in the I<ctx>
 parameter.
@@ -151,6 +165,10 @@ The key object should have been previously generated, loaded or imported into
 the provider using the key management (OSSL_OP_KEYMGMT) operation (see
 provider-keymgmt(7)>.
 
+OSSL_FUNC_kem_auth_decapsulate_init() is similiar to
+OSSL_FUNC_kem_decapsulate_init(), but also passes an additional authentication
+key I<provauthkey> which cannot be NULL.
+
 OSSL_FUNC_kem_decapsulate() performs the actual decapsulation itself.
 A previously initialised asymmetric kem context is passed in the I<ctx>
 parameter.
@@ -201,9 +219,12 @@ L<provider(7)>
 
 The provider KEM interface was introduced in OpenSSL 3.0.
 
+OSSL_FUNC_kem_auth_encapsulate_init() and OSSL_FUNC_kem_auth_decapsulate_init()
+were added in OpenSSL 3.1.
+
 =head1 COPYRIGHT
 
-Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
 
 Licensed under the Apache License 2.0 (the "License").  You may not use
 this file except in compliance with the License.  You can obtain a copy
index 62163b31ac13e94d2f78e7c015b6e1a774da0fd8..bb3cb0f979138ed88f2bfc47f0f6f9590faafa43 100644 (file)
@@ -95,6 +95,7 @@ char *ossl_ec_pt_format_id2name(int id);
 
 char *ossl_ec_check_group_type_id2name(int flags);
 int ossl_ec_set_check_group_type_from_name(EC_KEY *ec, const char *name);
-
+int ossl_ec_generate_key_dhkem(EC_KEY *eckey,
+                               const unsigned char *ikm, size_t ikmlen);
 # endif /* OPENSSL_NO_EC */
 #endif
index 48b95fa5bac81f32c1c937b212b0907820457266..79026b6c41671fcc565950b3db9ef51de25e2c7b 100644 (file)
@@ -83,6 +83,9 @@ unsigned char *ossl_ecx_key_allocate_privkey(ECX_KEY *key);
 void ossl_ecx_key_free(ECX_KEY *key);
 int ossl_ecx_key_up_ref(ECX_KEY *key);
 ECX_KEY *ossl_ecx_key_dup(const ECX_KEY *key, int selection);
+int ossl_ecx_compute_key(ECX_KEY *peer, ECX_KEY *priv, size_t keylen,
+                         unsigned char *secret, size_t *secretlen,
+                         size_t outlen);
 
 int ossl_x25519(uint8_t out_shared_key[32], const uint8_t private_key[32],
                 const uint8_t peer_public_value[32]);
diff --git a/include/crypto/hpke.h b/include/crypto/hpke.h
new file mode 100644 (file)
index 0000000..e3596fd
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_CRYPTO_HPKE_H
+# define OSSL_CRYPTO_HPKE_H
+# pragma once
+
+/* Constants from RFC 9180 Section 7.1 and 7.3 */
+#define OSSL_HPKE_MAX_SECRET 64
+#define OSSL_HPKE_MAX_PUBLIC 133
+#define OSSL_HPKE_MAX_PRIVATE 66
+#define OSSL_HPKE_MAX_NONCE 12
+#define OSSL_HPKE_MAX_KDF_INPUTLEN 64
+
+int ossl_hpke_kdf_extract(EVP_KDF_CTX *kctx,
+                          unsigned char *prk, size_t prklen,
+                          const unsigned char *salt, size_t saltlen,
+                          const unsigned char *ikm, size_t ikmlen);
+
+int ossl_hpke_kdf_expand(EVP_KDF_CTX *kctx,
+                         unsigned char *okm, size_t okmlen,
+                         const unsigned char *prk, size_t prklen,
+                         const unsigned char *info, size_t infolen);
+
+int ossl_hpke_labeled_extract(EVP_KDF_CTX *kctx,
+                              unsigned char *prk, size_t prklen,
+                              const unsigned char *salt, size_t saltlen,
+                              const unsigned char *suiteid, size_t suiteidlen,
+                              const char *label,
+                              const unsigned char *ikm, size_t ikmlen);
+int ossl_hpke_labeled_expand(EVP_KDF_CTX *kctx,
+                             unsigned char *okm, size_t okmlen,
+                             const unsigned char *prk, size_t prklen,
+                             const unsigned char *suiteid, size_t suiteidlen,
+                             const char *label,
+                             const unsigned char *info, size_t infolen);
+
+EVP_KDF_CTX *ossl_kdf_ctx_create(const char *kdfname, const char *mdname,
+                                 OSSL_LIB_CTX *libctx, const char *propq);
+
+#endif
index 11e3c861aa447319dc27a7b615856295bc0e3a9e..114e2667cebcc550a4b2d80078fb000566c14bb2 100644 (file)
@@ -815,16 +815,24 @@ OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, asym_cipher_settable_ctx_params,
 # define OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS     9
 # define OSSL_FUNC_KEM_SET_CTX_PARAMS         10
 # define OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS    11
+# define OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT  12
+# define OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT  13
 
 OSSL_CORE_MAKE_FUNC(void *, kem_newctx, (void *provctx))
 OSSL_CORE_MAKE_FUNC(int, kem_encapsulate_init, (void *ctx, void *provkey,
                                                 const OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(int, kem_auth_encapsulate_init, (void *ctx, void *provkey,
+                                                     void *authprivkey,
+                                                     const OSSL_PARAM params[]))
 OSSL_CORE_MAKE_FUNC(int, kem_encapsulate, (void *ctx,
                                            unsigned char *out, size_t *outlen,
                                            unsigned char *secret,
                                            size_t *secretlen))
 OSSL_CORE_MAKE_FUNC(int, kem_decapsulate_init, (void *ctx, void *provkey,
                                                 const OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(int, kem_auth_decapsulate_init, (void *ctx, void *provkey,
+                                                     void *authpubkey,
+                                                     const OSSL_PARAM params[]))
 OSSL_CORE_MAKE_FUNC(int, kem_decapsulate, (void *ctx,
                                            unsigned char *out, size_t *outlen,
                                            const unsigned char *in, size_t inlen))
index 75ec85f22a2b463263866fcdf22151229b800629..487b928e44e05c0a11428138e5e559e37403f3e0 100644 (file)
@@ -413,6 +413,9 @@ extern "C" {
 #define OSSL_PKEY_PARAM_RSA_MGF1_DIGEST      OSSL_PKEY_PARAM_MGF1_DIGEST
 #define OSSL_PKEY_PARAM_RSA_PSS_SALTLEN      "saltlen"
 
+/* EC, X25519 and X448 Key generation parameters */
+#define OSSL_PKEY_PARAM_DHKEM_IKM        "dhkem-ikm"
+
 /* Key generation parameters */
 #define OSSL_PKEY_PARAM_FFC_TYPE         "type"
 #define OSSL_PKEY_PARAM_FFC_PBITS        "pbits"
@@ -507,9 +510,11 @@ extern "C" {
 
 /* KEM parameters */
 #define OSSL_KEM_PARAM_OPERATION            "operation"
+#define OSSL_KEM_PARAM_IKME                 "ikme"
 
 /* OSSL_KEM_PARAM_OPERATION values */
 #define OSSL_KEM_PARAM_OPERATION_RSASVE     "RSASVE"
+#define OSSL_KEM_PARAM_OPERATION_DHKEM      "DHKEM"
 
 /* Capabilities */
 
index d9b3db6ae6afeb0bf9f7278d8fba9aa1ef5dbeb2..8a9f61d73edc0d6e14cec0b51116f5ff74fb2678 100644 (file)
@@ -1925,14 +1925,17 @@ int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer);
 int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen);
 
 int EVP_PKEY_encapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[]);
+int EVP_PKEY_auth_encapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpriv,
+                                   const OSSL_PARAM params[]);
 int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
                          unsigned char *wrappedkey, size_t *wrappedkeylen,
                          unsigned char *genkey, size_t *genkeylen);
 int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[]);
+int EVP_PKEY_auth_decapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpub,
+                                   const OSSL_PARAM params[]);
 int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx,
                          unsigned char *unwrapped, size_t *unwrappedlen,
                          const unsigned char *wrapped, size_t wrappedlen);
-
 typedef int EVP_PKEY_gen_cb(EVP_PKEY_CTX *ctx);
 
 int EVP_PKEY_fromdata_init(EVP_PKEY_CTX *ctx);
index edfcc97bae7be5d2599a3c28f067c8bef30fc670..dc9521b235bf6b3a23ad020b002fc600dea28629 100644 (file)
@@ -412,6 +412,11 @@ static const OSSL_ALGORITHM deflt_asym_cipher[] = {
 
 static const OSSL_ALGORITHM deflt_asym_kem[] = {
     { PROV_NAMES_RSA, "provider=default", ossl_rsa_asym_kem_functions },
+#ifndef OPENSSL_NO_EC
+    { PROV_NAMES_X25519, "provider=default", ossl_ecx_asym_kem_functions },
+    { PROV_NAMES_X448, "provider=default", ossl_ecx_asym_kem_functions },
+    { PROV_NAMES_EC, "provider=default", ossl_ec_asym_kem_functions },
+#endif
     { NULL, NULL, NULL }
 };
 
index 2ba9090c8b5a4f8ac4edd5c595c47846c4776674..7e223f28c8fad279f61722ed6e417033d62917cc 100644 (file)
@@ -17,9 +17,6 @@
 #include "crypto/ecx.h"
 #include "prov/implementations.h"
 #include "prov/providercommon.h"
-#ifdef S390X_EC_ASM
-# include "s390x_arch.h"
-#endif
 
 static OSSL_FUNC_keyexch_newctx_fn x25519_newctx;
 static OSSL_FUNC_keyexch_newctx_fn x448_newctx;
@@ -120,65 +117,8 @@ static int ecx_derive(void *vecxctx, unsigned char *secret, size_t *secretlen,
 
     if (!ossl_prov_is_running())
         return 0;
-
-    if (ecxctx->key == NULL
-            || ecxctx->key->privkey == NULL
-            || ecxctx->peerkey == NULL) {
-        ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
-        return 0;
-    }
-
-    if (!ossl_assert(ecxctx->keylen == X25519_KEYLEN
-            || ecxctx->keylen == X448_KEYLEN)) {
-        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
-        return 0;
-    }
-
-    if (secret == NULL) {
-        *secretlen = ecxctx->keylen;
-        return 1;
-    }
-    if (outlen < ecxctx->keylen) {
-        ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
-        return 0;
-    }
-
-    if (ecxctx->keylen == X25519_KEYLEN) {
-#ifdef S390X_EC_ASM
-        if (OPENSSL_s390xcap_P.pcc[1]
-                & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X25519)) {
-            if (s390x_x25519_mul(secret, ecxctx->peerkey->pubkey,
-                                 ecxctx->key->privkey) == 0) {
-                ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
-                return 0;
-            }
-        } else
-#endif
-        if (ossl_x25519(secret, ecxctx->key->privkey,
-                        ecxctx->peerkey->pubkey) == 0) {
-            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
-            return 0;
-        }
-    } else {
-#ifdef S390X_EC_ASM
-        if (OPENSSL_s390xcap_P.pcc[1]
-                & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X448)) {
-            if (s390x_x448_mul(secret, ecxctx->peerkey->pubkey,
-                               ecxctx->key->privkey) == 0) {
-                ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
-                return 0;
-            }
-        } else
-#endif
-        if (ossl_x448(secret, ecxctx->key->privkey,
-                      ecxctx->peerkey->pubkey) == 0) {
-            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
-            return 0;
-        }
-    }
-
-    *secretlen = ecxctx->keylen;
-    return 1;
+    return ossl_ecx_compute_key(ecxctx->peerkey, ecxctx->key, ecxctx->keylen,
+                                secret, secretlen, outlen);
 }
 
 static void ecx_freectx(void *vecxctx)
diff --git a/providers/implementations/include/prov/ecx.h b/providers/implementations/include/prov/ecx.h
new file mode 100644 (file)
index 0000000..3427d15
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/types.h"
+
+#ifndef OPENSSL_NO_EC
+
+/* RFC 9180 Labels used for Extract and Expand operations */
+
+/* ASCII: "eae_prk", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_EAE_PRK "\x65\x61\x65\x5F\x70\x72\x6B"
+/* ASCII: "shared_secret", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_SHARED_SECRET "\x73\x68\x61\x72\x65\x64\x5F\x73\x65\x63\x72\x65\x74"
+/* ASCII: "dkp_prk", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_DKP_PRK "\x64\x6B\x70\x5F\x70\x72\x6B"
+/* ASCII: "candidate", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_CANDIDATE "\x63\x61\x6E\x64\x69\x64\x61\x74\x65"
+/* ASCII: "sk", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_SK "\x73\x6B"
+
+int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout,
+                                  const unsigned char *ikm, size_t ikmlen);
+int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *privout,
+                                 const unsigned char *ikm, size_t ikmlen);
+#endif
index a6ac602d41c2b9850106454bfec2ed971e9808ef..9ea14162c7cf7f6de8ffce766b8cc7093d29ed1f 100644 (file)
@@ -334,6 +334,8 @@ extern const OSSL_DISPATCH ossl_sm2_asym_cipher_functions[];
 
 /* Asym Key encapsulation  */
 extern const OSSL_DISPATCH ossl_rsa_asym_kem_functions[];
+extern const OSSL_DISPATCH ossl_ecx_asym_kem_functions[];
+extern const OSSL_DISPATCH ossl_ec_asym_kem_functions[];
 
 /* Encoders */
 extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_der_encoder_functions[];
index dbb1b7d750d9efea12a77b527a21ba7c0f7631e2..6addb9b2c79045009a96f7e622fdb9b8b76d3973 100644 (file)
@@ -2,5 +2,10 @@
 # switch each to the Legacy provider when needed.
 
 $RSA_KEM_GOAL=../../libdefault.a ../../libfips.a
+$EC_KEM_GOAL=../../libdefault.a
 
 SOURCE[$RSA_KEM_GOAL]=rsa_kem.c
+
+IF[{- !$disabled{ec} -}]
+  SOURCE[$EC_KEM_GOAL]=ecx_kem.c kem_util.c ec_kem.c
+ENDIF
diff --git a/providers/implementations/kem/ec_kem.c b/providers/implementations/kem/ec_kem.c
new file mode 100644 (file)
index 0000000..57dcea4
--- /dev/null
@@ -0,0 +1,841 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * The following implementation is part of RFC 9180 related to DHKEM using
+ * EC keys (i.e. P-256, P-384 and P-521)
+ * References to Sections in the comments below refer to RFC 9180.
+ */
+
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/ec.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/kdf.h>
+#include <openssl/rand.h>
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+
+#include "crypto/hpke.h"
+#include "crypto/ec.h"
+#include "prov/ecx.h"
+#include "eckem.h"
+
+/*
+ * Used to store constants from Section 7.1 "Table 2 KEM IDs"
+ * and the bitmask for curves described in Section 7.1.3 DeriveKeyPair
+ */
+typedef struct {
+    const char *curve;
+    const char *kdfdigestname;
+    uint16_t kemid;
+    size_t secretlen;       /* Nsecret = Nh */
+    size_t encodedpublen;
+    size_t encodedprivlen;
+    uint8_t bitmask;
+} DHKEM_ALG;
+
+typedef struct {
+    EC_KEY *recipient_key;
+    EC_KEY *sender_authkey;
+    OSSL_LIB_CTX *libctx;
+    char *propq;
+    unsigned int mode;
+    unsigned int op;
+    unsigned char *ikm;
+    size_t ikmlen;
+    const char *kdfname;
+    const DHKEM_ALG *alg;
+} PROV_EC_CTX;
+
+static OSSL_FUNC_kem_newctx_fn eckem_newctx;
+static OSSL_FUNC_kem_encapsulate_init_fn eckem_encapsulate_init;
+static OSSL_FUNC_kem_auth_encapsulate_init_fn eckem_auth_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn eckem_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn eckem_decapsulate_init;
+static OSSL_FUNC_kem_auth_decapsulate_init_fn eckem_auth_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn eckem_decapsulate;
+static OSSL_FUNC_kem_freectx_fn eckem_freectx;
+static OSSL_FUNC_kem_set_ctx_params_fn eckem_set_ctx_params;
+static OSSL_FUNC_kem_settable_ctx_params_fn eckem_settable_ctx_params;
+
+/* See Section 7.1 "Table 2 KEM IDs" */
+static const DHKEM_ALG dhkem_alg[] = {
+    { "P-256", "SHA256", 0x0010, 32, 65,  32, 0xFF },
+    { "P-384", "SHA384", 0x0011, 48, 97,  48, 0xFF },
+    { "P-521", "SHA512", 0x0012, 64, 133, 66, 0x01 },
+    { NULL }
+};
+
+/* Return an object containing KEM constants associated with a EC curve name */
+static const DHKEM_ALG *dhkem_ec_find_alg(const char *curve)
+{
+    int i;
+
+    for (i = 0; dhkem_alg[i].curve != NULL; ++i) {
+        if (OPENSSL_strcasecmp(curve, dhkem_alg[i].curve) == 0)
+            return &dhkem_alg[i];
+    }
+    ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
+    return NULL;
+}
+
+static int eckey_check(const EC_KEY *ec, int requires_privatekey)
+{
+    int rv = 0;
+    BN_CTX *bnctx = NULL;
+    BIGNUM *rem = NULL;
+    const BIGNUM *priv = EC_KEY_get0_private_key(ec);
+    const EC_POINT *pub = EC_KEY_get0_public_key(ec);
+
+    /* Keys always require a public component */
+    if (pub == NULL) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+        return 0;
+    }
+    if (priv == NULL) {
+        return (requires_privatekey == 0);
+    } else {
+        /* If there is a private key, check that is non zero (mod order) */
+        const EC_GROUP *group = EC_KEY_get0_group(ec);
+        const BIGNUM *order = EC_GROUP_get0_order(group);
+
+        bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
+        rem = BN_new();
+
+        if (order != NULL && rem != NULL && bnctx != NULL) {
+             rv = BN_mod(rem, priv, order, bnctx)
+                  && !BN_is_zero(rem);
+        }
+    }
+    BN_free(rem);
+    BN_CTX_free(bnctx);
+    return rv;
+}
+
+/* Returns NULL if the curve is not supported */
+static const char *ec_curvename_get0(const EC_KEY *ec)
+{
+    const EC_GROUP *group = EC_KEY_get0_group(ec);
+
+    return EC_curve_nid2nist(EC_GROUP_get_curve_name(group));
+}
+
+/*
+ * Set the recipient key, and free any existing key.
+ * ec can be NULL.
+ * The ec key may have only a private or public component
+ * (but it must have a group).
+ */
+static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)
+{
+    EC_KEY_free(ctx->recipient_key);
+    ctx->recipient_key = NULL;
+
+    if (ec != NULL) {
+        const char *curve = ec_curvename_get0(ec);
+
+        if (curve == NULL)
+            return -2;
+        ctx->alg = dhkem_ec_find_alg(curve);
+        if (ctx->alg == NULL)
+            return -2;
+        if (!EC_KEY_up_ref(ec))
+            return 0;
+        ctx->recipient_key = ec;
+        ctx->kdfname = "HKDF";
+    }
+    return 1;
+}
+
+/*
+ * Set the senders auth key, and free any existing auth key.
+ * ec can be NULL.
+ */
+static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec)
+{
+    EC_KEY_free(ctx->sender_authkey);
+    ctx->sender_authkey = NULL;
+
+    if (ec != NULL) {
+        if (!EC_KEY_up_ref(ec))
+            return 0;
+        ctx->sender_authkey = ec;
+    }
+    return 1;
+}
+
+/*
+ * Serializes a encoded public key buffer into a EC public key.
+ * Params:
+ *     in Contains the group.
+ *     pubbuf The encoded public key buffer
+ * Returns: The created public EC key, or NULL if there is an error.
+ */
+static EC_KEY *eckey_frompub(EC_KEY *in,
+                             const unsigned char *pubbuf, size_t pubbuflen)
+{
+    EC_KEY *key;
+
+    key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in));
+    if (key == NULL)
+        goto err;
+    if (!EC_KEY_set_group(key, EC_KEY_get0_group(in)))
+        goto err;
+    if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL))
+        goto err;
+    return key;
+err:
+    EC_KEY_free(key);
+    return NULL;
+}
+
+/*
+ * Deserialises a EC public key into a encoded byte array.
+ * Returns: 1 if successful or 0 otherwise.
+ */
+static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen,
+                           size_t maxoutlen)
+{
+    const EC_POINT *pub;
+    const EC_GROUP *group;
+
+    group = EC_KEY_get0_group(ec);
+    pub = EC_KEY_get0_public_key(ec);
+    *outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED,
+                                 out, maxoutlen, NULL);
+    return *outlen != 0;
+}
+
+static void *eckem_newctx(void *provctx)
+{
+    PROV_EC_CTX *ctx =  OPENSSL_zalloc(sizeof(PROV_EC_CTX));
+
+    if (ctx == NULL)
+        return NULL;
+    ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+    return ctx;
+}
+
+static void eckem_freectx(void *vectx)
+{
+    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;
+
+    OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+    recipient_key_set(ctx, NULL);
+    sender_authkey_set(ctx, NULL);
+    OPENSSL_free(ctx);
+}
+
+static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)
+{
+    int ret;
+    BN_CTX *ctx = NULL;
+    const EC_GROUP *group1 = EC_KEY_get0_group(key1);
+    const EC_GROUP *group2 = EC_KEY_get0_group(key2);
+
+    ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));
+    if (ctx == NULL)
+        return 0;
+
+    ret = group1 != NULL
+          && group2 != NULL
+          && EC_GROUP_cmp(group1, group2, ctx) == 0;
+    if (!ret)
+        ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
+    BN_CTX_free(ctx);
+    return ret;
+}
+
+static int eckem_init(void *vctx, int operation, void *vec, void *vauth,
+                      const OSSL_PARAM params[])
+{
+    int rv;
+    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+    EC_KEY *ec = vec;
+    EC_KEY *auth = vauth;
+
+    if (!ossl_prov_is_running())
+        return 0;
+
+    if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))
+        return 0;
+    rv = recipient_key_set(ctx, ec);
+    if (rv <= 0)
+        return rv;
+
+    if (auth != NULL) {
+        if (!ossl_ec_match_params(ec, auth)
+            || !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
+            || !sender_authkey_set(ctx, auth))
+        return 0;
+    }
+
+    ctx->op = operation;
+    return eckem_set_ctx_params(vctx, params);
+}
+
+static int eckem_encapsulate_init(void *vctx, void *vec,
+                                   const OSSL_PARAM params[])
+{
+    return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);
+}
+
+static int eckem_decapsulate_init(void *vctx, void *vec,
+                                   const OSSL_PARAM params[])
+{
+    return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);
+}
+
+static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
+                                       const OSSL_PARAM params[])
+{
+    return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
+}
+
+static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
+                                       const OSSL_PARAM params[])
+{
+    return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
+}
+
+static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+    const OSSL_PARAM *p;
+    int mode;
+
+    if (params == NULL)
+        return 1;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
+    if (p != NULL) {
+        void *tmp = NULL;
+        size_t tmplen = 0;
+
+        if (p->data != NULL && p->data_size != 0) {
+            if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
+                return 0;
+        }
+        OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+        /* Set the ephemeral seed */
+        ctx->ikm = tmp;
+        ctx->ikmlen = tmplen;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        mode = ossl_eckem_modename2id(p->data);
+        if (mode == KEM_MODE_UNDEFINED)
+            return 0;
+        ctx->mode = mode;
+    }
+    return 1;
+}
+
+static const OSSL_PARAM known_settable_eckem_ctx_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,
+                                                   ossl_unused void *provctx)
+{
+    return known_settable_eckem_ctx_params;
+}
+
+/*
+ * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
+ */
+static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
+                                    unsigned char *okm, size_t okmlen,
+                                    uint16_t kemid,
+                                    const unsigned char *dhkm, size_t dhkmlen,
+                                    const unsigned char *kemctx,
+                                    size_t kemctxlen)
+{
+    uint8_t suiteid[5];
+    uint8_t prk[EVP_MAX_MD_SIZE];
+    size_t prklen = okmlen;
+    int ret;
+
+    if (prklen > sizeof(prk))
+        return 0;
+
+    ossl_dhkem_getsuiteid(suiteid, kemid);
+
+    ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
+                                    NULL, 0, suiteid, sizeof(suiteid),
+                                    OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
+          && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
+                                      suiteid, sizeof(suiteid),
+                                      OSSL_DHKEM_LABEL_SHARED_SECRET,
+                                      kemctx, kemctxlen);
+    OPENSSL_cleanse(prk, prklen);
+    return ret;
+}
+
+/*
+ * See Section 7.1.3 DeriveKeyPair.
+ *
+ * This function is used by ec keygen.
+ * (For this reason it does not use any of the state stored in PROV_EC_CTX).
+ *
+ * Params:
+ *     ec An initialized ec key.
+ *     priv The buffer to store the generated private key into (it is assumed
+ *          this is of length alg->encodedprivlen).
+ *     ikm buffer containing the input key material (seed). This must be set.
+ *     ikmlen size of the ikm buffer in bytes
+ * Returns:
+ *     1 if successful or 0 otherwise.
+ */
+int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
+                                 const unsigned char *ikm, size_t ikmlen)
+{
+    int ret = 0;
+    EVP_KDF_CTX *kdfctx = NULL;
+    uint8_t suiteid[5];
+    unsigned char prk[OSSL_HPKE_MAX_SECRET];
+    unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];
+    const BIGNUM *order;
+    unsigned char counter = 0;
+    const DHKEM_ALG *alg;
+    const char *curve = ec_curvename_get0(ec);
+
+    if (curve == NULL)
+        return -2;
+
+    alg = dhkem_ec_find_alg(curve);
+    if (alg == NULL)
+        return -2;
+
+    kdfctx = ossl_kdf_ctx_create("HKDF", alg->kdfdigestname,
+                                 ossl_ec_key_get_libctx(ec),
+                                 ossl_ec_key_get0_propq(ec));
+    if (kdfctx == NULL)
+        return 0;
+
+    /* ikmlen should have a length of at least Nsk */
+    if (ikmlen < alg->encodedprivlen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
+                       "ikm length is :%zu, should be at least %zu",
+                       ikmlen, alg->encodedprivlen);
+        goto err;
+    }
+
+    ossl_dhkem_getsuiteid(suiteid, alg->kemid);
+
+    if (!ossl_hpke_labeled_extract(kdfctx, prk, alg->secretlen,
+                                   NULL, 0, suiteid, sizeof(suiteid),
+                                   OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
+        goto err;
+
+    order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));
+    do {
+        if (!ossl_hpke_labeled_expand(kdfctx, privbuf, alg->encodedprivlen,
+                                      prk, alg->secretlen,
+                                      suiteid, sizeof(suiteid),
+                                      OSSL_DHKEM_LABEL_CANDIDATE,
+                                      &counter, 1))
+            goto err;
+        privbuf[0] &= alg->bitmask;
+        if (BN_bin2bn(privbuf, alg->encodedprivlen, priv) == NULL)
+            goto err;
+        if (counter == 0xFF) {
+            ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+            goto err;
+        }
+        counter++;
+    } while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);
+    ret = 1;
+err:
+    OPENSSL_cleanse(prk, sizeof(prk));
+    OPENSSL_cleanse(privbuf, sizeof(privbuf));
+    EVP_KDF_CTX_free(kdfctx);
+    return ret;
+}
+
+/*
+ * Do a keygen operation without having to use EVP_PKEY.
+ * Params:
+ *     ctx Context object
+ *     ikm The seed material - if this is NULL, then a random seed is used.
+ * Returns:
+ *     The generated EC key, or NULL on failure.
+ */
+static EC_KEY *derivekey(PROV_EC_CTX *ctx,
+                         const unsigned char *ikm, size_t ikmlen)
+{
+    int ret = 0;
+    EC_KEY *key;
+    unsigned char *seed = (unsigned char *)ikm;
+    size_t seedlen = ikmlen;
+    unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
+
+    key = EC_KEY_new_ex(ctx->libctx, ctx->propq);
+    if (key == NULL)
+        goto err;
+    if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))
+        goto err;
+
+    /* Generate a random seed if there is no input ikm */
+    if (seed == NULL || seedlen == 0) {
+        seedlen = ctx->alg->encodedprivlen;
+        if (seedlen > sizeof(tmpbuf))
+            goto err;
+        if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
+            goto err;
+        seed = tmpbuf;
+    }
+    ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);
+err:
+    if (seed != ikm)
+        OPENSSL_cleanse(seed, seedlen);
+    if (ret <= 0) {
+        EC_KEY_free(key);
+        key = NULL;
+    }
+    return key;
+}
+
+/*
+ * Before doing a key exchange the public key of the peer needs to be checked
+ * Note that the group check is not done here as we have already checked
+ * that it only uses one of the approved curve names when the key was set.
+ *
+ * Returns 1 if the public key is valid, or 0 if it fails.
+ */
+static int check_publickey(const EC_KEY *pub)
+{
+    int ret = 0;
+    BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));
+
+    if (bnctx == NULL)
+        return 0;
+    ret = ossl_ec_key_public_check(pub, bnctx);
+    BN_CTX_free(bnctx);
+
+    return ret;
+}
+
+/*
+ * Do an ecdh key exchange.
+ * dhkm = DH(sender, peer)
+ *
+ * NOTE: Instead of using EVP_PKEY_derive() API's, we use EC_KEY operations
+ *       to avoid messy conversions back to EVP_PKEY.
+ *
+ * Returns the size of the secret if successful, or 0 otherwise,
+ */
+static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer,
+                           unsigned char *out, size_t maxout,
+                           unsigned int secretsz)
+{
+    const EC_GROUP *group = EC_KEY_get0_group(sender);
+    size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;
+
+    if (secretlen != secretsz || secretlen > maxout) {
+        ERR_raise_data(ERR_LIB_PROV,  PROV_R_BAD_LENGTH, "secretsz invalid");
+        return 0;
+    }
+
+    if (!check_publickey(peer))
+        return 0;
+    return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),
+                            sender, NULL) > 0;
+}
+
+/*
+ * Derive a secret using ECDH (code is shared by the encap and decap)
+ *
+ * dhkm = Concat(ecdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
+ * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
+ * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
+ *
+ * Params:
+ *     ctx Object that contains algorithm state and constants.
+ *     secret The returned secret (with a length ctx->alg->secretlen bytes).
+ *     privkey1 A private key used for ECDH key derivation.
+ *     peerkey1 A public key used for ECDH key derivation with privkey1
+ *     privkey2 A optional private key used for a second ECDH key derivation.
+ *              It can be NULL.
+ *     peerkey2 A optional public key used for a second ECDH key derivation
+ *              with privkey2,. It can be NULL.
+ *     sender_pub The senders public key in encoded form.
+ *     recipient_pub The recipients public key in encoded form.
+ * Notes:
+ *     The second ecdh() is only used for the HPKE auth modes when both privkey2
+ *     and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
+ */
+static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
+                         const EC_KEY *privkey1, const EC_KEY *peerkey1,
+                         const EC_KEY *privkey2, const EC_KEY *peerkey2,
+                         const unsigned char *sender_pub,
+                         const unsigned char *recipient_pub)
+{
+    int ret = 0;
+    EVP_KDF_CTX *kdfctx = NULL;
+    unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];
+    unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];
+    unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
+    size_t sender_authpublen;
+    size_t kemctxlen = 0, dhkmlen = 0;
+    size_t encodedpublen = ctx->alg->encodedpublen;
+    size_t encodedprivlen = ctx->alg->encodedprivlen;
+    int auth = ctx->sender_authkey != NULL;
+
+    if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen))
+        goto err;
+    dhkmlen = encodedprivlen;
+    kemctxlen = 2 * encodedpublen;
+
+    /* Concat the optional second ECDH (used for Auth) */
+    if (auth) {
+        /* Get the public key of the auth sender in encoded form */
+        if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,
+                             &sender_authpublen, sizeof(sender_authpub)))
+            goto err;
+        if (sender_authpublen != encodedpublen) {
+            ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+                           "Invalid sender auth public key");
+            goto err;
+        }
+        if (!generate_ecdhkm(privkey2, peerkey2,
+                             dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
+                             encodedprivlen))
+            goto err;
+        dhkmlen += encodedprivlen;
+        kemctxlen += encodedpublen;
+    }
+    if (kemctxlen > sizeof(kemctx))
+        goto err;
+
+    /* kemctx is the concat of both sides encoded public key */
+    memcpy(kemctx, sender_pub, ctx->alg->encodedpublen);
+    memcpy(kemctx + ctx->alg->encodedpublen, recipient_pub,
+           ctx->alg->encodedpublen);
+    if (auth)
+        memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);
+    kdfctx = ossl_kdf_ctx_create(ctx->kdfname, ctx->alg->kdfdigestname,
+                                 ctx->libctx, ctx->propq);
+    if (kdfctx == NULL)
+        goto err;
+    if (!dhkem_extract_and_expand(kdfctx, secret, ctx->alg->secretlen,
+                                  ctx->alg->kemid, dhkm, dhkmlen,
+                                  kemctx, kemctxlen))
+        goto err;
+    ret = 1;
+err:
+    OPENSSL_cleanse(dhkm, dhkmlen);
+    EVP_KDF_CTX_free(kdfctx);
+    return ret;
+}
+
+/*
+ * Do a DHKEM encapsulate operation.
+ *
+ * See Section 4.1 Encap() and AuthEncap()
+ *
+ * Params:
+ *     ctx A context object holding the recipients public key and the
+ *         optional senders auth private key.
+ *     enc A buffer to return the senders ephemeral public key.
+ *         Setting this to NULL allows the enclen and secretlen to return
+ *         values, without calculating the secret.
+ *     enclen Passes in the max size of the enc buffer and returns the
+ *            encoded public key length.
+ *     secret A buffer to return the calculated shared secret.
+ *     secretlen Passes in the max size of the secret buffer and returns the
+ *               secret length.
+ * Returns: 1 on success or 0 otherwise.
+ */
+static int dhkem_encap(PROV_EC_CTX *ctx,
+                       unsigned char *enc, size_t *enclen,
+                       unsigned char *secret, size_t *secretlen)
+{
+    int ret = 0;
+    EC_KEY *sender_ephemkey = NULL;
+    unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];
+    unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
+    size_t sender_publen, recipient_publen;
+
+    if (enc == NULL) {
+        if (enclen == NULL && secretlen == NULL)
+            return 0;
+        if (enclen != NULL)
+            *enclen = ctx->alg->encodedpublen;
+        if (secretlen != NULL)
+            *secretlen = ctx->alg->secretlen;
+       return 1;
+    }
+
+    if (*secretlen < ctx->alg->secretlen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+        return 0;
+    }
+    if (*enclen < ctx->alg->encodedpublen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
+        return 0;
+    }
+
+    /* Create an ephemeral key */
+    sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
+    if (sender_ephemkey == NULL)
+        goto err;
+    if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,
+                         sizeof(sender_pub))
+            || !ecpubkey_todata(ctx->recipient_key, recipient_pub,
+                                &recipient_publen, sizeof(recipient_pub)))
+        goto err;
+
+    if (sender_publen != ctx->alg->encodedpublen
+            || recipient_publen != sender_publen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
+        goto err;
+    }
+
+    if (!derive_secret(ctx, secret,
+                       sender_ephemkey, ctx->recipient_key,
+                       ctx->sender_authkey, ctx->recipient_key,
+                       sender_pub, recipient_pub))
+        goto err;
+
+    /* Return the senders ephemeral public key in encoded form */
+    memcpy(enc, sender_pub, sender_publen);
+    *enclen = sender_publen;
+    *secretlen = ctx->alg->secretlen;
+    ret = 1;
+err:
+    EC_KEY_free(sender_ephemkey);
+    return ret;
+}
+
+/*
+ * Do a DHKEM decapsulate operation.
+ * See Section 4.1 Decap() and Auth Decap()
+ *
+ * Params:
+ *     ctx A context object holding the recipients private key and the
+ *         optional senders auth public key.
+ *     secret A buffer to return the calculated shared secret. Setting this to
+ *            NULL can be used to return the secretlen.
+ *     secretlen Passes in the max size of the secret buffer and returns the
+ *               secret length.
+ *     enc A buffer containing the senders ephemeral public key that was returned
+ *         from dhkem_encap().
+ *     enclen The length in bytes of enc.
+ * Returns: 1 If the shared secret is returned or 0 on error.
+ */
+static int dhkem_decap(PROV_EC_CTX *ctx,
+                       unsigned char *secret, size_t *secretlen,
+                       const unsigned char *enc, size_t enclen)
+{
+    int ret = 0;
+    EC_KEY *sender_ephempubkey = NULL;
+    unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
+    size_t recipient_publen;
+    size_t encodedpublen = ctx->alg->encodedpublen;
+
+    if (secret == NULL) {
+        *secretlen = ctx->alg->secretlen;
+        return 1;
+    }
+
+    if (*secretlen < ctx->alg->secretlen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+        return 0;
+    }
+    if (enclen != encodedpublen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
+        return 0;
+    }
+
+    sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);
+    if (sender_ephempubkey == NULL)
+        goto err;
+    if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,
+                         sizeof(recipient_pub)))
+        goto err;
+    if (recipient_publen != encodedpublen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");
+        goto err;
+    }
+
+    if (!derive_secret(ctx, secret,
+                       ctx->recipient_key, sender_ephempubkey,
+                       ctx->recipient_key, ctx->sender_authkey,
+                       enc, recipient_pub))
+        goto err;
+    *secretlen = ctx->alg->secretlen;
+    ret = 1;
+err:
+    EC_KEY_free(sender_ephempubkey);
+    return ret;
+}
+
+static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
+                             unsigned char *secret, size_t *secretlen)
+{
+    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+
+    switch (ctx->mode) {
+        case KEM_MODE_DHKEM:
+            return dhkem_encap(ctx, out, outlen, secret, secretlen);
+        default:
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+            return -2;
+    }
+}
+
+static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
+                             const unsigned char *in, size_t inlen)
+{
+    PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+
+    switch (ctx->mode) {
+        case KEM_MODE_DHKEM:
+            return dhkem_decap(ctx, out, outlen, in, inlen);
+        default:
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+            return -2;
+    }
+}
+
+const OSSL_DISPATCH ossl_ec_asym_kem_functions[] = {
+    { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))eckem_newctx },
+    { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
+      (void (*)(void))eckem_encapsulate_init },
+    { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))eckem_encapsulate },
+    { OSSL_FUNC_KEM_DECAPSULATE_INIT,
+      (void (*)(void))eckem_decapsulate_init },
+    { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))eckem_decapsulate },
+    { OSSL_FUNC_KEM_FREECTX, (void (*)(void))eckem_freectx },
+    { OSSL_FUNC_KEM_SET_CTX_PARAMS,
+      (void (*)(void))eckem_set_ctx_params },
+    { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
+      (void (*)(void))eckem_settable_ctx_params },
+    { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
+      (void (*)(void))eckem_auth_encapsulate_init },
+    { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
+      (void (*)(void))eckem_auth_decapsulate_init },
+    { 0, NULL }
+};
diff --git a/providers/implementations/kem/eckem.h b/providers/implementations/kem/eckem.h
new file mode 100644 (file)
index 0000000..44fdde8
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define KEM_MODE_UNDEFINED   0
+#define KEM_MODE_DHKEM       1
+
+int ossl_eckem_modename2id(const char *name);
+void ossl_dhkem_getsuiteid(unsigned char suiteid[5], uint16_t kemid);
diff --git a/providers/implementations/kem/ecx_kem.c b/providers/implementations/kem/ecx_kem.c
new file mode 100644 (file)
index 0000000..979035f
--- /dev/null
@@ -0,0 +1,706 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * The following implementation is part of RFC 9180 related to DHKEM using
+ * ECX keys (i.e. X25519 and X448)
+ * References to Sections in the comments below refer to RFC 9180.
+ */
+
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/kdf.h>
+#include <openssl/err.h>
+#include <openssl/sha.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+#include "prov/ecx.h"
+#include "crypto/ecx.h"
+#include "crypto/hpke.h"
+#include "eckem.h"
+
+#define MAX_ECX_KEYLEN X448_KEYLEN
+
+/* KEM identifiers from Section 7.1 "Table 2 KEM IDs" */
+#define KEMID_X25519_HKDF_SHA256 0x20
+#define KEMID_X448_HKDF_SHA512   0x21
+
+typedef struct {
+    ECX_KEY *recipient_key;
+    ECX_KEY *sender_authkey;
+    OSSL_LIB_CTX *libctx;
+    char *propq;
+    unsigned int mode;
+    unsigned int op;
+    uint16_t kemid;
+    unsigned char *ikm;
+    size_t ikmlen;
+    const char *kdfname;
+    const char *kdfdigestname;
+    size_t sharedsecretlen;
+    size_t keylen;
+} PROV_ECX_CTX;
+
+static OSSL_FUNC_kem_newctx_fn ecxkem_newctx;
+static OSSL_FUNC_kem_encapsulate_init_fn ecxkem_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn ecxkem_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn ecxkem_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn ecxkem_decapsulate;
+static OSSL_FUNC_kem_freectx_fn ecxkem_freectx;
+static OSSL_FUNC_kem_set_ctx_params_fn ecxkem_set_ctx_params;
+static OSSL_FUNC_kem_auth_encapsulate_init_fn ecxkem_auth_encapsulate_init;
+static OSSL_FUNC_kem_auth_decapsulate_init_fn ecxkem_auth_decapsulate_init;
+
+/*
+ * Set KEM values as specified in Section 7.1 "Table 2 KEM IDs"
+ * There is only one set of values for X25519 and X448.
+ * Additional values could be set via set_params if required.
+ */
+static void get_kem_values(ECX_KEY *ecx, uint16_t *kemid,
+                           const char **kdfdigestname, size_t *secretlen,
+                           size_t *keylen)
+{
+    if (ecx->type == ECX_KEY_TYPE_X25519) {
+        *kemid = KEMID_X25519_HKDF_SHA256;
+        *kdfdigestname = "SHA256";
+        *secretlen = SHA256_DIGEST_LENGTH;
+    } else {
+        *kemid = KEMID_X448_HKDF_SHA512;
+        *kdfdigestname = "SHA512";
+        *secretlen = SHA512_DIGEST_LENGTH;
+    }
+    /* ECX keys have the same length for public and private keys */
+    *keylen = ecx->keylen;
+}
+
+/*
+ * Set the recipient key, and free any existing key.
+ * ecx can be NULL. The ecx key may have only a private or public component.
+ */
+static int recipient_key_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
+{
+    ossl_ecx_key_free(ctx->recipient_key);
+    ctx->recipient_key = NULL;
+    if (ecx != NULL) {
+        get_kem_values(ecx, &ctx->kemid, &ctx->kdfdigestname,
+                       &ctx->sharedsecretlen, &ctx->keylen);
+        ctx->kdfname = "HKDF";
+        if (!ossl_ecx_key_up_ref(ecx))
+            return 0;
+        ctx->recipient_key = ecx;
+    }
+    return 1;
+}
+
+/*
+ * Set the senders auth key, and free any existing auth key.
+ * ecx can be NULL.
+ */
+static int sender_authkey_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
+{
+    ossl_ecx_key_free(ctx->sender_authkey);
+    ctx->sender_authkey = NULL;
+
+    if (ecx != NULL) {
+        if (!ossl_ecx_key_up_ref(ecx))
+            return 0;
+        ctx->sender_authkey = ecx;
+    }
+    return 1;
+}
+
+/*
+ * Serialize a public key from byte array's for the encoded public keys.
+ * ctx is used to access the key type.
+ * Returns: The created ECX_KEY or NULL on error.
+ */
+static ECX_KEY *ecxkey_pubfromdata(PROV_ECX_CTX *ctx,
+                                   const unsigned char *pubbuf, size_t pubbuflen)
+{
+    ECX_KEY *ecx = NULL;
+    OSSL_PARAM params[2], *p = params;
+
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+                                             (char *)pubbuf, pubbuflen);
+    *p = OSSL_PARAM_construct_end();
+
+    ecx = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 1, ctx->propq);
+    if (ecx == NULL)
+        return NULL;
+    if (ossl_ecx_key_fromdata(ecx, params, 0) <= 0) {
+        ossl_ecx_key_free(ecx);
+        ecx = NULL;
+    }
+    return ecx;
+}
+
+static unsigned char *ecx_pubkey(ECX_KEY *ecx)
+{
+    if (ecx == NULL || !ecx->haspubkey) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+        return 0;
+    }
+    return ecx->pubkey;
+}
+
+static void *ecxkem_newctx(void *provctx)
+{
+    PROV_ECX_CTX *ctx =  OPENSSL_zalloc(sizeof(PROV_ECX_CTX));
+
+    if (ctx == NULL)
+        return NULL;
+    ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+    return ctx;
+}
+
+static void ecxkem_freectx(void *vectx)
+{
+    PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vectx;
+
+    OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+    recipient_key_set(ctx, NULL);
+    sender_authkey_set(ctx, NULL);
+    OPENSSL_free(ctx);
+}
+
+static int ecx_match_params(const ECX_KEY *key1, const ECX_KEY *key2)
+{
+    return (key1->type == key2->type && key1->keylen == key2->keylen);
+}
+
+static int ecx_key_check(const ECX_KEY *ecx, int requires_privatekey)
+{
+    if (ecx->privkey == NULL)
+        return (requires_privatekey == 0);
+    return 1;
+}
+
+static int ecxkem_init(void *vecxctx, int operation, void *vecx, void *vauth,
+                       ossl_unused const OSSL_PARAM params[])
+{
+    int rv;
+    PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vecxctx;
+    ECX_KEY *ecx = vecx;
+    ECX_KEY *auth = vauth;
+
+    if (!ossl_prov_is_running())
+        return 0;
+
+    if (!ecx_key_check(ecx, operation == EVP_PKEY_OP_DECAPSULATE))
+        return 0;
+    rv = recipient_key_set(ctx, ecx);
+    if (rv <= 0)
+        return rv;
+
+    if (auth != NULL) {
+        if (!ecx_match_params(auth, ctx->recipient_key)
+                || !ecx_key_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
+                || !sender_authkey_set(ctx, auth))
+            return 0;
+    }
+
+    ctx->op = operation;
+    return ecxkem_set_ctx_params(vecxctx, params);
+}
+
+static int ecxkem_encapsulate_init(void *vecxctx, void *vecx,
+                                   const OSSL_PARAM params[])
+{
+    return ecxkem_init(vecxctx, EVP_PKEY_OP_ENCAPSULATE, vecx, NULL, params);
+}
+
+static int ecxkem_decapsulate_init(void *vecxctx, void *vecx,
+                                   const OSSL_PARAM params[])
+{
+    return ecxkem_init(vecxctx, EVP_PKEY_OP_DECAPSULATE, vecx, NULL, params);
+}
+
+static int ecxkem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
+                                        const OSSL_PARAM params[])
+{
+    return ecxkem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
+}
+
+static int ecxkem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
+                                        const OSSL_PARAM params[])
+{
+    return ecxkem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
+}
+
+static int ecxkem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+    PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
+    const OSSL_PARAM *p;
+    int mode;
+
+    if (ctx == NULL)
+        return 0;
+    if (params == NULL)
+        return 1;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
+    if (p != NULL) {
+        void *tmp = NULL;
+        size_t tmplen = 0;
+
+        if (p->data != NULL && p->data_size != 0) {
+            if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
+                return 0;
+        }
+        OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+        ctx->ikm = tmp;
+        ctx->ikmlen = tmplen;
+    }
+    p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
+    if (p != NULL) {
+        if (p->data_type != OSSL_PARAM_UTF8_STRING)
+            return 0;
+        mode = ossl_eckem_modename2id(p->data);
+        if (mode == KEM_MODE_UNDEFINED)
+            return 0;
+        ctx->mode = mode;
+    }
+    return 1;
+}
+
+static const OSSL_PARAM known_settable_ecxkem_ctx_params[] = {
+    OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
+    OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ecxkem_settable_ctx_params(ossl_unused void *vctx,
+                                                   ossl_unused void *provctx)
+{
+    return known_settable_ecxkem_ctx_params;
+}
+
+/*
+ * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
+ */
+static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
+                                    unsigned char *okm, size_t okmlen,
+                                    uint16_t kemid,
+                                    const unsigned char *dhkm, size_t dhkmlen,
+                                    const unsigned char *kemctx,
+                                    size_t kemctxlen)
+{
+    uint8_t suiteid[5];
+    uint8_t prk[EVP_MAX_MD_SIZE];
+    size_t prklen = okmlen; /* Nh */
+    int ret;
+
+    if (prklen > sizeof(prk))
+        return 0;
+
+    ossl_dhkem_getsuiteid(suiteid, kemid);
+
+    ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
+                                    NULL, 0, suiteid, sizeof(suiteid),
+                                    OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
+          && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
+                                      suiteid, sizeof(suiteid),
+                                      OSSL_DHKEM_LABEL_SHARED_SECRET,
+                                      kemctx, kemctxlen);
+    OPENSSL_cleanse(prk, prklen);
+    return ret;
+}
+
+/*
+ * See Section 7.1.3 DeriveKeyPair.
+ *
+ * This function is used by ecx keygen.
+ * (For this reason it does not use any of the state stored in PROV_ECX_CTX).
+ *
+ * Params:
+ *     ecx An initialized ecx key.
+ *     privout The buffer to store the generated private key into (it is assumed
+ *             this is of length ecx->keylen).
+ *     ikm buffer containing the input key material (seed). This must be non NULL.
+ *     ikmlen size of the ikm buffer in bytes
+ * Returns:
+ *     1 if successful or 0 otherwise.
+ */
+int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout,
+                                  const unsigned char *ikm, size_t ikmlen)
+{
+    int ret = 0;
+    EVP_KDF_CTX *kdfctx = NULL;
+    unsigned char prk[EVP_MAX_MD_SIZE];
+    uint16_t kemid;
+    const char *kdfdigestname;
+    uint8_t suiteid[5];
+    size_t prklen, keylen;
+
+    get_kem_values(ecx, &kemid, &kdfdigestname, &prklen, &keylen);
+
+    /* ikmlen should have a length of at least Nsk */
+    if (ikmlen < keylen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
+                       "ikm length is :%zu, should be at least %zu",
+                       ikmlen, keylen);
+        goto err;
+    }
+
+    kdfctx = ossl_kdf_ctx_create("HKDF", kdfdigestname, ecx->libctx, ecx->propq);
+    if (kdfctx == NULL)
+        return 0;
+
+    ossl_dhkem_getsuiteid(suiteid, kemid);
+
+    if (!ossl_hpke_labeled_extract(kdfctx, prk, prklen,
+                                   NULL, 0, suiteid, sizeof(suiteid),
+                                   OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
+        goto err;
+
+    if (!ossl_hpke_labeled_expand(kdfctx, privout, keylen, prk, prklen,
+                                  suiteid, sizeof(suiteid), OSSL_DHKEM_LABEL_SK,
+                                  NULL, 0))
+        goto err;
+    ret = 1;
+err:
+    OPENSSL_cleanse(prk, sizeof(prk));
+    EVP_KDF_CTX_free(kdfctx);
+    return ret;
+}
+
+/*
+ * Do a keygen operation without having to use EVP_PKEY.
+ * Params:
+ *     ctx Context object
+ *     ikm The seed material - if this is NULL, then a random seed is used.
+ * Returns:
+ *     The generated ECX key, or NULL on failure.
+ */
+static ECX_KEY *derivekey(PROV_ECX_CTX *ctx,
+                          const unsigned char *ikm, size_t ikmlen)
+{
+    int ok = 0;
+    ECX_KEY *key;
+    unsigned char *privkey;
+    unsigned char *seed = (unsigned char *)ikm;
+    size_t seedlen = ikmlen;
+    unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
+
+    key = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 0, ctx->propq);
+    if (key == NULL)
+        return NULL;
+    privkey = ossl_ecx_key_allocate_privkey(key);
+    if (privkey == NULL)
+        goto err;
+
+    /* Generate a random seed if there is no input ikm */
+    if (seed == NULL || seedlen == 0) {
+        if (ctx->keylen > sizeof(tmpbuf))
+            goto err;
+        if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, ctx->keylen, 0) <= 0)
+            goto err;
+        seed = tmpbuf;
+        seedlen = ctx->keylen;
+    }
+    if (!ossl_ecx_dhkem_derive_private(key, privkey, seed, seedlen))
+        goto err;
+    if (!ossl_ecx_public_from_private(key))
+        goto err;
+    key->haspubkey = 1;
+    ok = 1;
+err:
+    if (!ok) {
+        ossl_ecx_key_free(key);
+        key = NULL;
+    }
+    if (seed != ikm)
+        OPENSSL_cleanse(seed, seedlen);
+    return key;
+}
+
+/*
+ * Do an ecxdh key exchange.
+ * dhkm = DH(sender, peer)
+ *
+ * NOTE: Instead of using EVP_PKEY_derive() API's, we use ECX_KEY operations
+ *       to avoid messy conversions back to EVP_PKEY.
+ *
+ * Returns the size of the secret if successful, or 0 otherwise,
+ */
+static int generate_ecxdhkm(const ECX_KEY *sender, const ECX_KEY *peer,
+                           unsigned char *out,  size_t maxout,
+                           unsigned int secretsz)
+{
+    size_t len = 0;
+
+    /* NOTE: ossl_ecx_compute_key checks for shared secret being all zeros */
+    return ossl_ecx_compute_key((ECX_KEY *)peer, (ECX_KEY *)sender,
+                                 sender->keylen, out, &len, maxout);
+}
+
+/*
+ * Derive a secret using ECXDH (code is shared by the encap and decap)
+ *
+ * dhkm = Concat(ecxdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
+ * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
+ * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
+ *
+ * Params:
+ *     ctx Object that contains algorithm state and constants.
+ *     secret The returned secret (with a length ctx->alg->secretlen bytes).
+ *     privkey1 A private key used for ECXDH key derivation.
+ *     peerkey1 A public key used for ECXDH key derivation with privkey1
+ *     privkey2 A optional private key used for a second ECXDH key derivation.
+ *              It can be NULL.
+ *     peerkey2 A optional public key used for a second ECXDH key derivation
+ *              with privkey2,. It can be NULL.
+ *     sender_pub The senders public key in encoded form.
+ *     recipient_pub The recipients public key in encoded form.
+ * Notes:
+ *     The second ecdh() is only used for the HPKE auth modes when both privkey2
+ *     and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
+ */
+static int derive_secret(PROV_ECX_CTX *ctx, unsigned char *secret,
+                         const ECX_KEY *privkey1, const ECX_KEY *peerkey1,
+                         const ECX_KEY *privkey2, const ECX_KEY *peerkey2,
+                         const unsigned char *sender_pub,
+                         const unsigned char *recipient_pub)
+{
+    int ret = 0;
+    EVP_KDF_CTX *kdfctx = NULL;
+    unsigned char *sender_authpub = NULL;
+    unsigned char dhkm[MAX_ECX_KEYLEN * 2];
+    unsigned char kemctx[MAX_ECX_KEYLEN * 3];
+    size_t kemctxlen = 0, dhkmlen = 0;
+    size_t encodedkeylen = ctx->keylen;
+    int auth = ctx->sender_authkey != NULL;
+
+    if (!generate_ecxdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedkeylen))
+        goto err;
+    dhkmlen = encodedkeylen;
+
+    /* Concat the optional second ECXDH (used for Auth) */
+    if (auth) {
+        if (!generate_ecxdhkm(privkey2, peerkey2,
+                              dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
+                              encodedkeylen))
+            goto err;
+        /* Get the public key of the auth sender in encoded form */
+        sender_authpub = ecx_pubkey(ctx->sender_authkey);
+        if (sender_authpub == NULL)
+            goto err;
+        dhkmlen += encodedkeylen;
+    }
+    kemctxlen = encodedkeylen + dhkmlen;
+    if (kemctxlen > sizeof(kemctx))
+        goto err;
+
+    /* kemctx is the concat of both sides encoded public key */
+    memcpy(kemctx, sender_pub, encodedkeylen);
+    memcpy(kemctx + encodedkeylen, recipient_pub, encodedkeylen);
+    if (auth)
+        memcpy(kemctx + 2 * encodedkeylen, sender_authpub, encodedkeylen);
+    kdfctx = ossl_kdf_ctx_create(ctx->kdfname, ctx->kdfdigestname,
+                                 ctx->libctx, ctx->propq);
+    if (kdfctx == NULL)
+        goto err;
+    if (!dhkem_extract_and_expand(kdfctx, secret, ctx->sharedsecretlen,
+                                  ctx->kemid, dhkm, dhkmlen,
+                                  kemctx, kemctxlen))
+        goto err;
+    ret = 1;
+err:
+    OPENSSL_cleanse(dhkm, dhkmlen);
+    EVP_KDF_CTX_free(kdfctx);
+    return ret;
+}
+
+/*
+ * Do a DHKEM encapsulate operation.
+ *
+ * See Section 4.1 Encap() and AuthEncap()
+ *
+ * Params:
+ *     ctx A context object holding the recipients public key and the
+ *         optional senders auth private key.
+ *     enc A buffer to return the senders ephemeral public key.
+ *         Setting this to NULL allows the enclen and secretlen to return
+ *         values, without calculating the secret.
+ *     enclen Passes in the max size of the enc buffer and returns the
+ *            encoded public key length.
+ *     secret A buffer to return the calculated shared secret.
+ *     secretlen Passes in the max size of the secret buffer and returns the
+ *               secret length.
+ * Returns: 1 on success or 0 otherwise.
+ */
+static int dhkem_encap(PROV_ECX_CTX *ctx,
+                       unsigned char *enc, size_t *enclen,
+                       unsigned char *secret, size_t *secretlen)
+{
+    int ret = 0;
+    ECX_KEY *sender_ephemkey = NULL;
+    unsigned char *sender_ephempub, *recipient_pub;
+
+    if (enc == NULL) {
+        if (enclen == NULL && secretlen == NULL)
+            return 0;
+        if (enclen != NULL)
+            *enclen = ctx->keylen;
+        if (secretlen != NULL)
+            *secretlen = ctx->sharedsecretlen;
+       return 1;
+    }
+
+    if (*secretlen < ctx->sharedsecretlen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+        return 0;
+    }
+    if (*enclen < ctx->keylen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
+        return 0;
+    }
+
+    /* Create an ephemeral key */
+    sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
+
+    sender_ephempub = ecx_pubkey(sender_ephemkey);
+    recipient_pub = ecx_pubkey(ctx->recipient_key);
+    if (sender_ephempub == NULL || recipient_pub == NULL)
+        goto err;
+
+    if (!derive_secret(ctx, secret,
+                       sender_ephemkey, ctx->recipient_key,
+                       ctx->sender_authkey, ctx->recipient_key,
+                       sender_ephempub, recipient_pub))
+        goto err;
+
+    /* Return the public part of the ephemeral key */
+    memcpy(enc, sender_ephempub, ctx->keylen);
+    *enclen = ctx->keylen;
+    *secretlen = ctx->sharedsecretlen;
+    ret = 1;
+err:
+    ossl_ecx_key_free(sender_ephemkey);
+    return ret;
+}
+
+/*
+ * Do a DHKEM decapsulate operation.
+ * See Section 4.1 Decap() and Auth Decap()
+ *
+ * Params:
+ *     ctx A context object holding the recipients private key and the
+ *         optional senders auth public key.
+ *     secret A buffer to return the calculated shared secret. Setting this to
+ *            NULL can be used to return the secretlen.
+ *     secretlen Passes in the max size of the secret buffer and returns the
+ *               secret length.
+ *     enc A buffer containing the senders ephemeral public key that was returned
+ *         from dhkem_encap().
+ *     enclen The length in bytes of enc.
+ * Returns: 1 If the shared secret is returned or 0 on error.
+ */
+static int dhkem_decap(PROV_ECX_CTX *ctx,
+                       unsigned char *secret, size_t *secretlen,
+                       const unsigned char *enc, size_t enclen)
+{
+    int ret = 0;
+    ECX_KEY *recipient_privkey = ctx->recipient_key;
+    ECX_KEY *sender_ephempubkey = NULL;
+    unsigned char *recipient_pub;
+
+    if (secret == NULL) {
+        *secretlen = ctx->sharedsecretlen;
+        return 1;
+    }
+    if (*secretlen < ctx->sharedsecretlen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+        return 0;
+    }
+    if (enclen != ctx->keylen) {
+        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
+        return 0;
+    }
+
+    /* Get the public part of the ephemeral key created by encap */
+    sender_ephempubkey = ecxkey_pubfromdata(ctx, enc, enclen);
+    if (sender_ephempubkey == NULL)
+        goto err;
+
+    recipient_pub = ecx_pubkey(recipient_privkey);
+    if (recipient_pub == NULL)
+        goto err;
+
+    if (!derive_secret(ctx, secret,
+                       ctx->recipient_key, sender_ephempubkey,
+                       ctx->recipient_key, ctx->sender_authkey,
+                       enc, recipient_pub))
+        goto err;
+
+    *secretlen = ctx->sharedsecretlen;
+    ret = 1;
+err:
+    ossl_ecx_key_free(sender_ephempubkey);
+    return ret;
+}
+
+static int ecxkem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
+                              unsigned char *secret, size_t *secretlen)
+{
+    PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
+
+    switch (ctx->mode) {
+        case KEM_MODE_DHKEM:
+            return dhkem_encap(ctx, out, outlen, secret, secretlen);
+        default:
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+            return -2;
+    }
+}
+
+static int ecxkem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
+                              const unsigned char *in, size_t inlen)
+{
+    PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
+
+    switch (ctx->mode) {
+        case KEM_MODE_DHKEM:
+            return dhkem_decap(vctx, out, outlen, in, inlen);
+        default:
+            ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+            return -2;
+    }
+}
+
+const OSSL_DISPATCH ossl_ecx_asym_kem_functions[] = {
+    { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))ecxkem_newctx },
+    { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
+      (void (*)(void))ecxkem_encapsulate_init },
+    { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))ecxkem_encapsulate },
+    { OSSL_FUNC_KEM_DECAPSULATE_INIT,
+      (void (*)(void))ecxkem_decapsulate_init },
+    { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))ecxkem_decapsulate },
+    { OSSL_FUNC_KEM_FREECTX, (void (*)(void))ecxkem_freectx },
+    { OSSL_FUNC_KEM_SET_CTX_PARAMS,
+      (void (*)(void))ecxkem_set_ctx_params },
+    { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
+      (void (*)(void))ecxkem_settable_ctx_params },
+    { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
+      (void (*)(void))ecxkem_auth_encapsulate_init },
+    { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
+      (void (*)(void))ecxkem_auth_decapsulate_init },
+    { 0, NULL }
+};
diff --git a/providers/implementations/kem/kem_util.c b/providers/implementations/kem/kem_util.c
new file mode 100644 (file)
index 0000000..8ce2854
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h> /* for memcpy() */
+#include <openssl/core_names.h>
+#include <openssl/crypto.h>
+#include "eckem.h"
+
+typedef struct {
+    unsigned int id;
+    const char *mode;
+} KEM_MODE;
+
+static const KEM_MODE eckem_modename_id_map[] = {
+    { KEM_MODE_DHKEM, OSSL_KEM_PARAM_OPERATION_DHKEM },
+    { 0, NULL }
+};
+
+int ossl_eckem_modename2id(const char *name)
+{
+    size_t i;
+
+    if (name == NULL)
+        return KEM_MODE_UNDEFINED;
+
+    for (i = 0; eckem_modename_id_map[i].mode != NULL; ++i) {
+        if (OPENSSL_strcasecmp(name, eckem_modename_id_map[i].mode) == 0)
+            return eckem_modename_id_map[i].id;
+    }
+    return KEM_MODE_UNDEFINED;
+}
+
+/* suiteid = concat("KEM", I2OSP(kem_id, 2)) */
+void ossl_dhkem_getsuiteid(unsigned char suiteid[5], uint16_t kemid)
+{
+    memcpy(suiteid, "KEM", 3);
+    suiteid[3] = kemid >> 8;
+    suiteid[4] = kemid & 0xFF;
+}
index 9d51194cce5566231d9d502688b4ce452c271551..3f2653f8ae398605e17ea874a7ac759676966876 100644 (file)
@@ -981,6 +981,8 @@ struct ec_gen_ctx {
     int selection;
     int ecdh_mode;
     EC_GROUP *gen_group;
+    unsigned char *dhkem_ikm;
+    size_t dhkem_ikmlen;
 };
 
 static void *ec_gen_init(void *provctx, int selection,
@@ -1114,6 +1116,9 @@ static int ec_gen_set_params(void *genctx, const OSSL_PARAM params[])
     COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_EC_GENERATOR, gctx->gen,
                      gctx->gen_len);
 
+    COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_DHKEM_IKM, gctx->dhkem_ikm,
+                     gctx->dhkem_ikmlen);
+
     ret = 1;
 err:
     EC_GROUP_free(group);
@@ -1213,6 +1218,7 @@ static const OSSL_PARAM *ec_gen_settable_params(ossl_unused void *genctx,
         OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_ORDER, NULL, 0),
         OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_COFACTOR, NULL, 0),
         OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM, NULL, 0),
         OSSL_PARAM_END
     };
 
@@ -1266,14 +1272,22 @@ static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
     ret = ec_gen_assign_group(ec, gctx->gen_group);
 
     /* Whether you want it or not, you get a keypair, not just one half */
-    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
-        ret = ret && EC_KEY_generate_key(ec);
+    if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+#ifndef FIPS_MODULE
+        if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0)
+            ret = ret && ossl_ec_generate_key_dhkem(ec, gctx->dhkem_ikm,
+                                                    gctx->dhkem_ikmlen);
+        else
+#endif
+            ret = ret && EC_KEY_generate_key(ec);
+    }
 
     if (gctx->ecdh_mode != -1)
         ret = ret && ossl_ec_set_ecdh_cofactor_mode(ec, gctx->ecdh_mode);
 
     if (gctx->group_check != NULL)
-        ret = ret && ossl_ec_set_check_group_type_from_name(ec, gctx->group_check);
+        ret = ret && ossl_ec_set_check_group_type_from_name(ec,
+                                                            gctx->group_check);
     if (ret)
         return ec;
 err:
@@ -1341,6 +1355,7 @@ static void ec_gen_cleanup(void *genctx)
     if (gctx == NULL)
         return;
 
+    OPENSSL_clear_free(gctx->dhkem_ikm, gctx->dhkem_ikmlen);
     EC_GROUP_free(gctx->gen_group);
     BN_free(gctx->p);
     BN_free(gctx->a);
index 2a7f867aa56b3bdf6399b98d3795f7b7f99348b3..70fad0150ef5957b093a004a7bbbdb56d44a24cb 100644 (file)
@@ -22,6 +22,7 @@
 #include "prov/implementations.h"
 #include "prov/providercommon.h"
 #include "prov/provider_ctx.h"
+#include "prov/ecx.h"
 #ifdef S390X_EC_ASM
 # include "s390x_arch.h"
 # include <openssl/sha.h>   /* For SHA512_DIGEST_LENGTH */
@@ -78,6 +79,8 @@ struct ecx_gen_ctx {
     char *propq;
     ECX_KEY_TYPE type;
     int selection;
+    unsigned char *dhkem_ikm;
+    size_t dhkem_ikmlen;
 };
 
 #ifdef S390X_EC_ASM
@@ -558,6 +561,16 @@ static int ecx_gen_set_params(void *genctx, const OSSL_PARAM params[])
         if (gctx->propq == NULL)
             return 0;
     }
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DHKEM_IKM);
+    if (p != NULL) {
+        if (p->data_size != 0 && p->data != NULL) {
+            OPENSSL_free(gctx->dhkem_ikm);
+            gctx->dhkem_ikm = NULL;
+            if (!OSSL_PARAM_get_octet_string(p, (void **)&gctx->dhkem_ikm, 0,
+                                             &gctx->dhkem_ikmlen))
+                return 0;
+        }
+    }
 
     return 1;
 }
@@ -568,6 +581,7 @@ static const OSSL_PARAM *ecx_gen_settable_params(ossl_unused void *genctx,
     static OSSL_PARAM settable[] = {
         OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
         OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+        OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM, NULL, 0),
         OSSL_PARAM_END
     };
     return settable;
@@ -594,8 +608,21 @@ static void *ecx_gen(struct ecx_gen_ctx *gctx)
         ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
         goto err;
     }
-    if (RAND_priv_bytes_ex(gctx->libctx, privkey, key->keylen, 0) <= 0)
-        goto err;
+#ifndef FIPS_MODULE
+    if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) {
+        if (gctx->type == ECX_KEY_TYPE_ED25519
+                || gctx->type == ECX_KEY_TYPE_ED448)
+            goto err;
+        if (!ossl_ecx_dhkem_derive_private(key, privkey,
+                                           gctx->dhkem_ikm, gctx->dhkem_ikmlen))
+            goto err;
+    } else
+#endif
+    {
+        if (RAND_priv_bytes_ex(gctx->libctx, privkey, key->keylen, 0) <= 0)
+            goto err;
+    }
+
     switch (gctx->type) {
     case ECX_KEY_TYPE_X25519:
         privkey[0] &= 248;
@@ -691,6 +718,7 @@ static void ecx_gen_cleanup(void *genctx)
 {
     struct ecx_gen_ctx *gctx = genctx;
 
+    OPENSSL_clear_free(gctx->dhkem_ikm, gctx->dhkem_ikmlen);
     OPENSSL_free(gctx->propq);
     OPENSSL_free(gctx);
 }
index 69c4f12c57d4ea3636ced6acfc2e919dc50f95c6..ea4e77aec3042ccff0fa9d3b3733d651b5ccc7d9 100644 (file)
@@ -219,6 +219,10 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[provider_status_test]=../include ../apps/include
   DEPEND[provider_status_test]=../libcrypto.a libtestutil.a
 
+  SOURCE[evp_pkey_dhkem_test]=evp_pkey_dhkem_test.c
+  INCLUDE[evp_pkey_dhkem_test]=../include ../apps/include
+  DEPEND[evp_pkey_dhkem_test]=../libcrypto.a libtestutil.a
+
   IF[{- !$disabled{'deprecated-3.0'} -}]
     PROGRAMS{noinst}=igetest bftest casttest
 
@@ -658,7 +662,8 @@ IF[{- !$disabled{tests} -}]
       PROGRAMS{noinst}=sm4_internal_test
     ENDIF
     IF[{- !$disabled{ec} -}]
-      PROGRAMS{noinst}=ectest ec_internal_test curve448_internal_test
+      PROGRAMS{noinst}=ectest ec_internal_test curve448_internal_test \
+                       evp_pkey_dhkem_test
     ENDIF
     IF[{- !$disabled{cmac} -}]
       PROGRAMS{noinst}=cmactest
diff --git a/test/dhkem_test.inc b/test/dhkem_test.inc
new file mode 100644 (file)
index 0000000..51eb9dc
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+typedef struct {
+    const char *curvename;
+    /* seed */
+    const unsigned char *ikm;
+    size_t ikmlen;
+    /* expected public key */
+    const unsigned char *pub;
+    size_t publen;
+    /* expected private key */
+    const unsigned char *priv;
+    size_t privlen;
+} TEST_DERIVEKEY_DATA;
+
+typedef struct {
+    const char *curve;
+    /* The seed for the senders ephemeral key */
+    const unsigned char *ikmE;
+    size_t ikmElen;
+    /* Recipient key */
+    const unsigned char *rpub;
+    size_t rpublen;
+    const unsigned char *rpriv;
+    size_t rprivlen;
+    /* The senders generated ephemeral public key */
+    const unsigned char *expected_enc;
+    size_t expected_enclen;
+    /* The generated shared secret */
+    const unsigned char *expected_secret;
+    size_t expected_secretlen;
+    /* Senders Auth key */
+    const unsigned char *spub;
+    size_t spublen;
+    const unsigned char *spriv;
+    size_t sprivlen;
+} TEST_ENCAPDATA;
+
+static const char *dhkem_supported_curves[] = {
+    "P-256",
+    "P-384",
+    "P-521",
+    "X25519",
+    "X448"
+};
+
+/* TEST vectors extracted from RFC 9180 */
+
+/* Base test values */
+static const unsigned char x25519_ikme[] = {
+    0x72, 0x68, 0x60, 0x0d, 0x40, 0x3f, 0xce, 0x43,
+    0x15, 0x61, 0xae, 0xf5, 0x83, 0xee, 0x16, 0x13,
+    0x52, 0x7c, 0xff, 0x65, 0x5c, 0x13, 0x43, 0xf2,
+    0x98, 0x12, 0xe6, 0x67, 0x06, 0xdf, 0x32, 0x34
+};
+static const unsigned char x25519_ikme_priv[] = {
+    0x52, 0xc4, 0xa7, 0x58, 0xa8, 0x02, 0xcd, 0x8b,
+    0x93, 0x6e, 0xce, 0xea, 0x31, 0x44, 0x32, 0x79,
+    0x8d, 0x5b, 0xaf, 0x2d, 0x7e, 0x92, 0x35, 0xdc,
+    0x08, 0x4a, 0xb1, 0xb9, 0xcf, 0xa2, 0xf7, 0x36
+};
+static const unsigned char x25519_ikme_pub[] = {
+    0x37, 0xfd, 0xa3, 0x56, 0x7b, 0xdb, 0xd6, 0x28,
+    0xe8, 0x86, 0x68, 0xc3, 0xc8, 0xd7, 0xe9, 0x7d,
+    0x1d, 0x12, 0x53, 0xb6, 0xd4, 0xea, 0x6d, 0x44,
+    0xc1, 0x50, 0xf7, 0x41, 0xf1, 0xbf, 0x44, 0x31
+};
+static const unsigned char x25519_rpub[] = {
+    0x39, 0x48, 0xcf, 0xe0, 0xad, 0x1d, 0xdb, 0x69,
+    0x5d, 0x78, 0x0e, 0x59, 0x07, 0x71, 0x95, 0xda,
+    0x6c, 0x56, 0x50, 0x6b, 0x02, 0x73, 0x29, 0x79,
+    0x4a, 0xb0, 0x2b, 0xca, 0x80, 0x81, 0x5c, 0x4d
+};
+static const unsigned char x25519_rpriv[] = {
+    0x46, 0x12, 0xc5, 0x50, 0x26, 0x3f, 0xc8, 0xad,
+    0x58, 0x37, 0x5d, 0xf3, 0xf5, 0x57, 0xaa, 0xc5,
+    0x31, 0xd2, 0x68, 0x50, 0x90, 0x3e, 0x55, 0xa9,
+    0xf2, 0x3f, 0x21, 0xd8, 0x53, 0x4e, 0x8a, 0xc8
+};
+static const unsigned char x25519_expected_enc[] = {
+    0x37, 0xfd, 0xa3, 0x56, 0x7b, 0xdb, 0xd6, 0x28,
+    0xe8, 0x86, 0x68, 0xc3, 0xc8, 0xd7, 0xe9, 0x7d,
+    0x1d, 0x12, 0x53, 0xb6, 0xd4, 0xea, 0x6d, 0x44,
+    0xc1, 0x50, 0xf7, 0x41, 0xf1, 0xbf, 0x44, 0x31
+};
+static const unsigned char x25519_expected_secret[] = {
+    0xfe, 0x0e, 0x18, 0xc9, 0xf0, 0x24, 0xce, 0x43,
+    0x79, 0x9a, 0xe3, 0x93, 0xc7, 0xe8, 0xfe, 0x8f,
+    0xce, 0x9d, 0x21, 0x88, 0x75, 0xe8, 0x22, 0x7b,
+    0x01, 0x87, 0xc0, 0x4e, 0x7d, 0x2e, 0xa1, 0xfc
+};
+
+static const unsigned char x25519_auth_ikme[] = {
+    0x6e, 0x6d, 0x8f, 0x20, 0x0e, 0xa2, 0xfb, 0x20,
+    0xc3, 0x0b, 0x00, 0x3a, 0x8b, 0x4f, 0x43, 0x3d,
+    0x2f, 0x4e, 0xd4, 0xc2, 0x65, 0x8d, 0x5b, 0xc8,
+    0xce, 0x2f, 0xef, 0x71, 0x80, 0x59, 0xc9, 0xf7
+};
+static const unsigned char x25519_auth_rpub[] = {
+    0x16, 0x32, 0xd5, 0xc2, 0xf7, 0x1c, 0x2b, 0x38,
+    0xd0, 0xa8, 0xfc, 0xc3, 0x59, 0x35, 0x52, 0x00,
+    0xca, 0xa8, 0xb1, 0xff, 0xdf, 0x28, 0x61, 0x80,
+    0x80, 0x46, 0x6c, 0x90, 0x9c, 0xb6, 0x9b, 0x2e
+};
+static const unsigned char x25519_auth_rpriv[] = {
+    0xfd, 0xea, 0x67, 0xcf, 0x83, 0x1f, 0x1c, 0xa9,
+    0x8d, 0x8e, 0x27, 0xb1, 0xf6, 0xab, 0xeb, 0x5b,
+    0x77, 0x45, 0xe9, 0xd3, 0x53, 0x48, 0xb8, 0x0f,
+    0xa4, 0x07, 0xff, 0x69, 0x58, 0xf9, 0x13, 0x7e
+};
+static const unsigned char x25519_auth_spub[] = {
+    0x8b, 0x0c, 0x70, 0x87, 0x3d, 0xc5, 0xae, 0xcb,
+    0x7f, 0x9e, 0xe4, 0xe6, 0x24, 0x06, 0xa3, 0x97,
+    0xb3, 0x50, 0xe5, 0x70, 0x12, 0xbe, 0x45, 0xcf,
+    0x53, 0xb7, 0x10, 0x5a, 0xe7, 0x31, 0x79, 0x0b
+};
+static const unsigned char x25519_auth_spriv[] = {
+    0xdc, 0x4a, 0x14, 0x63, 0x13, 0xcc, 0xe6, 0x0a,
+    0x27, 0x8a, 0x53, 0x23, 0xd3, 0x21, 0xf0, 0x51,
+    0xc5, 0x70, 0x7e, 0x9c, 0x45, 0xba, 0x21, 0xa3,
+    0x47, 0x9f, 0xec, 0xdf, 0x76, 0xfc, 0x69, 0xdd
+};
+static const unsigned char x25519_auth_expected_enc[] = {
+    0x23, 0xfb, 0x95, 0x25, 0x71, 0xa1, 0x4a, 0x25,
+    0xe3, 0xd6, 0x78, 0x14, 0x0c, 0xd0, 0xe5, 0xeb,
+    0x47, 0xa0, 0x96, 0x1b, 0xb1, 0x8a, 0xfc, 0xf8,
+    0x58, 0x96, 0xe5, 0x45, 0x3c, 0x31, 0x2e, 0x76
+};
+static const unsigned char x25519_auth_expected_secret[] = {
+    0x2d, 0x6d, 0xb4, 0xcf, 0x71, 0x9d, 0xc7, 0x29,
+    0x3f, 0xcb, 0xf3, 0xfa, 0x64, 0x69, 0x07, 0x08,
+    0xe4, 0x4e, 0x2b, 0xeb, 0xc8, 0x1f, 0x84, 0x60,
+    0x86, 0x77, 0x95, 0x8c, 0x0d, 0x44, 0x48, 0xa7
+};
+
+static const unsigned char p256_ikme[] = {
+    0x42, 0x70, 0xe5, 0x4f, 0xfd, 0x08, 0xd7, 0x9d,
+    0x59, 0x28, 0x02, 0x0a, 0xf4, 0x68, 0x6d, 0x8f,
+    0x6b, 0x7d, 0x35, 0xdb, 0xe4, 0x70, 0x26, 0x5f,
+    0x1f, 0x5a, 0xa2, 0x28, 0x16, 0xce, 0x86, 0x0e
+};
+
+static const unsigned char p256_ikme_pub[] = {
+    0x04, 0xa9, 0x27, 0x19, 0xc6, 0x19, 0x5d, 0x50,
+    0x85, 0x10, 0x4f, 0x46, 0x9a, 0x8b, 0x98, 0x14,
+    0xd5, 0x83, 0x8f, 0xf7, 0x2b, 0x60, 0x50, 0x1e,
+    0x2c, 0x44, 0x66, 0xe5, 0xe6, 0x7b, 0x32, 0x5a,
+    0xc9, 0x85, 0x36, 0xd7, 0xb6, 0x1a, 0x1a, 0xf4,
+    0xb7, 0x8e, 0x5b, 0x7f, 0x95, 0x1c, 0x09, 0x00,
+    0xbe, 0x86, 0x3c, 0x40, 0x3c, 0xe6, 0x5c, 0x9b,
+    0xfc, 0xb9, 0x38, 0x26, 0x57, 0x22, 0x2d, 0x18,
+    0xc4
+};
+static const unsigned char p256_ikme_priv[] = {
+    0x49, 0x95, 0x78, 0x8e, 0xf4, 0xb9, 0xd6, 0x13,
+    0x2b, 0x24, 0x9c, 0xe5, 0x9a, 0x77, 0x28, 0x14,
+    0x93, 0xeb, 0x39, 0xaf, 0x37, 0x3d, 0x23, 0x6a,
+    0x1f, 0xe4, 0x15, 0xcb, 0x0c, 0x2d, 0x7b, 0xeb
+};
+
+static const unsigned char p256_ikmr[] = {
+    0x66, 0x8b, 0x37, 0x17, 0x1f, 0x10, 0x72, 0xf3,
+    0xcf, 0x12, 0xea, 0x8a, 0x23, 0x6a, 0x45, 0xdf,
+    0x23, 0xfc, 0x13, 0xb8, 0x2a, 0xf3, 0x60, 0x9a,
+    0xd1, 0xe3, 0x54, 0xf6, 0xef, 0x81, 0x75, 0x50
+};
+
+static const unsigned char p256_ikmr_pub[] = {
+    0x04, 0xfe, 0x8c, 0x19, 0xce, 0x09, 0x05, 0x19,
+    0x1e, 0xbc, 0x29, 0x8a, 0x92, 0x45, 0x79, 0x25,
+    0x31, 0xf2, 0x6f, 0x0c, 0xec, 0xe2, 0x46, 0x06,
+    0x39, 0xe8, 0xbc, 0x39, 0xcb, 0x7f, 0x70, 0x6a,
+    0x82, 0x6a, 0x77, 0x9b, 0x4c, 0xf9, 0x69, 0xb8,
+    0xa0, 0xe5, 0x39, 0xc7, 0xf6, 0x2f, 0xb3, 0xd3,
+    0x0a, 0xd6, 0xaa, 0x8f, 0x80, 0xe3, 0x0f, 0x1d,
+    0x12, 0x8a, 0xaf, 0xd6, 0x8a, 0x2c, 0xe7, 0x2e,
+    0xa0
+};
+
+static const unsigned char p256_ikmr_priv[] = {
+    0xf3, 0xce, 0x7f, 0xda, 0xe5, 0x7e, 0x1a, 0x31,
+    0x0d, 0x87, 0xf1, 0xeb, 0xbd, 0xe6, 0xf3, 0x28,
+    0xbe, 0x0a, 0x99, 0xcd, 0xbc, 0xad, 0xf4, 0xd6,
+    0x58, 0x9c, 0xf2, 0x9d, 0xe4, 0xb8, 0xff, 0xd2
+};
+
+static const unsigned char p256_expected_enc[] = {
+    0x04, 0xa9, 0x27, 0x19, 0xc6, 0x19, 0x5d, 0x50,
+    0x85, 0x10, 0x4f, 0x46, 0x9a, 0x8b, 0x98, 0x14,
+    0xd5, 0x83, 0x8f, 0xf7, 0x2b, 0x60, 0x50, 0x1e,
+    0x2c, 0x44, 0x66, 0xe5, 0xe6, 0x7b, 0x32, 0x5a,
+    0xc9, 0x85, 0x36, 0xd7, 0xb6, 0x1a, 0x1a, 0xf4,
+    0xb7, 0x8e, 0x5b, 0x7f, 0x95, 0x1c, 0x09, 0x00,
+    0xbe, 0x86, 0x3c, 0x40, 0x3c, 0xe6, 0x5c, 0x9b,
+    0xfc, 0xb9, 0x38, 0x26, 0x57, 0x22, 0x2d, 0x18,
+    0xc4
+};
+static const unsigned char p256_expected_secret[] = {
+    0xc0, 0xd2, 0x6a, 0xea, 0xb5, 0x36, 0x60, 0x9a,
+    0x57, 0x2b, 0x07, 0x69, 0x5d, 0x93, 0x3b, 0x58,
+    0x9d, 0xcf, 0x36, 0x3f, 0xf9, 0xd9, 0x3c, 0x93,
+    0xad, 0xea, 0x53, 0x7a, 0xea, 0xbb, 0x8c, 0xb8
+};
+
+static const unsigned char p521_ikme[] = {
+    0x7f, 0x06, 0xab, 0x82, 0x15, 0x10, 0x5f, 0xc4,
+    0x6a, 0xce, 0xeb, 0x2e, 0x3d, 0xc5, 0x02, 0x8b,
+    0x44, 0x36, 0x4f, 0x96, 0x04, 0x26, 0xeb, 0x0d,
+    0x8e, 0x40, 0x26, 0xc2, 0xf8, 0xb5, 0xd7, 0xe7,
+    0xa9, 0x86, 0x68, 0x8f, 0x15, 0x91, 0xab, 0xf5,
+    0xab, 0x75, 0x3c, 0x35, 0x7a, 0x5d, 0x6f, 0x04,
+    0x40, 0x41, 0x4b, 0x4e, 0xd4, 0xed, 0xe7, 0x13,
+    0x17, 0x77, 0x2a, 0xc9, 0x8d, 0x92, 0x39, 0xf7,
+    0x09, 0x04
+};
+
+static const unsigned char p521_ikme_pub[] = {
+    0x04, 0x01, 0x38, 0xb3, 0x85, 0xca, 0x16, 0xbb,
+    0x0d, 0x5f, 0xa0, 0xc0, 0x66, 0x5f, 0xbb, 0xd7,
+    0xe6, 0x9e, 0x3e, 0xe2, 0x9f, 0x63, 0x99, 0x1d,
+    0x3e, 0x9b, 0x5f, 0xa7, 0x40, 0xaa, 0xb8, 0x90,
+    0x0a, 0xae, 0xed, 0x46, 0xed, 0x73, 0xa4, 0x90,
+    0x55, 0x75, 0x84, 0x25, 0xa0, 0xce, 0x36, 0x50,
+    0x7c, 0x54, 0xb2, 0x9c, 0xc5, 0xb8, 0x5a, 0x5c,
+    0xee, 0x6b, 0xae, 0x0c, 0xf1, 0xc2, 0x1f, 0x27,
+    0x31, 0xec, 0xe2, 0x01, 0x3d, 0xc3, 0xfb, 0x7c,
+    0x8d, 0x21, 0x65, 0x4b, 0xb1, 0x61, 0xb4, 0x63,
+    0x96, 0x2c, 0xa1, 0x9e, 0x8c, 0x65, 0x4f, 0xf2,
+    0x4c, 0x94, 0xdd, 0x28, 0x98, 0xde, 0x12, 0x05,
+    0x1f, 0x1e, 0xd0, 0x69, 0x22, 0x37, 0xfb, 0x02,
+    0xb2, 0xf8, 0xd1, 0xdc, 0x1c, 0x73, 0xe9, 0xb3,
+    0x66, 0xb5, 0x29, 0xeb, 0x43, 0x6e, 0x98, 0xa9,
+    0x96, 0xee, 0x52, 0x2a, 0xef, 0x86, 0x3d, 0xd5,
+    0x73, 0x9d, 0x2f, 0x29, 0xb0
+};
+
+static const unsigned char p521_ikme_priv[] = {
+    0x01, 0x47, 0x84, 0xc6, 0x92, 0xda, 0x35, 0xdf,
+    0x6e, 0xcd, 0xe9, 0x8e, 0xe4, 0x3a, 0xc4, 0x25,
+    0xdb, 0xdd, 0x09, 0x69, 0xc0, 0xc7, 0x2b, 0x42,
+    0xf2, 0xe7, 0x08, 0xab, 0x9d, 0x53, 0x54, 0x15,
+    0xa8, 0x56, 0x9b, 0xda, 0xcf, 0xcc, 0x0a, 0x11,
+    0x4c, 0x85, 0xb8, 0xe3, 0xf2, 0x6a, 0xcf, 0x4d,
+    0x68, 0x11, 0x5f, 0x8c, 0x91, 0xa6, 0x61, 0x78,
+    0xcd, 0xbd, 0x03, 0xb7, 0xbc, 0xc5, 0x29, 0x1e,
+    0x37, 0x4b
+};
+
+static const unsigned char p521_ikmr_pub[] = {
+    0x04, 0x01, 0xb4, 0x54, 0x98, 0xc1, 0x71, 0x4e,
+    0x2d, 0xce, 0x16, 0x7d, 0x3c, 0xaf, 0x16, 0x2e,
+    0x45, 0xe0, 0x64, 0x2a, 0xfc, 0x7e, 0xd4, 0x35,
+    0xdf, 0x79, 0x02, 0xcc, 0xae, 0x0e, 0x84, 0xba,
+    0x0f, 0x7d, 0x37, 0x3f, 0x64, 0x6b, 0x77, 0x38,
+    0xbb, 0xbd, 0xca, 0x11, 0xed, 0x91, 0xbd, 0xea,
+    0xe3, 0xcd, 0xcb, 0xa3, 0x30, 0x1f, 0x24, 0x57,
+    0xbe, 0x45, 0x2f, 0x27, 0x1f, 0xa6, 0x83, 0x75,
+    0x80, 0xe6, 0x61, 0x01, 0x2a, 0xf4, 0x95, 0x83,
+    0xa6, 0x2e, 0x48, 0xd4, 0x4b, 0xed, 0x35, 0x0c,
+    0x71, 0x18, 0xc0, 0xd8, 0xdc, 0x86, 0x1c, 0x23,
+    0x8c, 0x72, 0xa2, 0xbd, 0xa1, 0x7f, 0x64, 0x70,
+    0x4f, 0x46, 0x4b, 0x57, 0x33, 0x8e, 0x7f, 0x40,
+    0xb6, 0x09, 0x59, 0x48, 0x0c, 0x0e, 0x58, 0xe6,
+    0x55, 0x9b, 0x19, 0x0d, 0x81, 0x66, 0x3e, 0xd8,
+    0x16, 0xe5, 0x23, 0xb6, 0xb6, 0xa4, 0x18, 0xf6,
+    0x6d, 0x24, 0x51, 0xec, 0x64
+};
+static const unsigned char p521_ikmr_priv[] = {
+    0x01, 0x46, 0x26, 0x80, 0x36, 0x9a, 0xe3, 0x75,
+    0xe4, 0xb3, 0x79, 0x10, 0x70, 0xa7, 0x45, 0x8e,
+    0xd5, 0x27, 0x84, 0x2f, 0x6a, 0x98, 0xa7, 0x9f,
+    0xf5, 0xe0, 0xd4, 0xcb, 0xde, 0x83, 0xc2, 0x71,
+    0x96, 0xa3, 0x91, 0x69, 0x56, 0x65, 0x55, 0x23,
+    0xa6, 0xa2, 0x55, 0x6a, 0x7a, 0xf6, 0x2c, 0x5c,
+    0xad, 0xab, 0xe2, 0xef, 0x9d, 0xa3, 0x76, 0x0b,
+    0xb2, 0x1e, 0x00, 0x52, 0x02, 0xf7, 0xb2, 0x46,
+    0x28, 0x47
+};
+
+static const unsigned char p521_expected_enc[] = {
+    0x04, 0x01, 0x38, 0xb3, 0x85, 0xca, 0x16, 0xbb,
+    0x0d, 0x5f, 0xa0, 0xc0, 0x66, 0x5f, 0xbb, 0xd7,
+    0xe6, 0x9e, 0x3e, 0xe2, 0x9f, 0x63, 0x99, 0x1d,
+    0x3e, 0x9b, 0x5f, 0xa7, 0x40, 0xaa, 0xb8, 0x90,
+    0x0a, 0xae, 0xed, 0x46, 0xed, 0x73, 0xa4, 0x90,
+    0x55, 0x75, 0x84, 0x25, 0xa0, 0xce, 0x36, 0x50,
+    0x7c, 0x54, 0xb2, 0x9c, 0xc5, 0xb8, 0x5a, 0x5c,
+    0xee, 0x6b, 0xae, 0x0c, 0xf1, 0xc2, 0x1f, 0x27,
+    0x31, 0xec, 0xe2, 0x01, 0x3d, 0xc3, 0xfb, 0x7c,
+    0x8d, 0x21, 0x65, 0x4b, 0xb1, 0x61, 0xb4, 0x63,
+    0x96, 0x2c, 0xa1, 0x9e, 0x8c, 0x65, 0x4f, 0xf2,
+    0x4c, 0x94, 0xdd, 0x28, 0x98, 0xde, 0x12, 0x05,
+    0x1f, 0x1e, 0xd0, 0x69, 0x22, 0x37, 0xfb, 0x02,
+    0xb2, 0xf8, 0xd1, 0xdc, 0x1c, 0x73, 0xe9, 0xb3,
+    0x66, 0xb5, 0x29, 0xeb, 0x43, 0x6e, 0x98, 0xa9,
+    0x96, 0xee, 0x52, 0x2a, 0xef, 0x86, 0x3d, 0xd5,
+    0x73, 0x9d, 0x2f, 0x29, 0xb0
+};
+static const unsigned char p521_expected_secret[] = {
+    0x77, 0x6a, 0xb4, 0x21, 0x30, 0x2f, 0x6e, 0xff,
+    0x7d, 0x7c, 0xb5, 0xcb, 0x1a, 0xda, 0xea, 0x0c,
+    0xd5, 0x08, 0x72, 0xc7, 0x1c, 0x2d, 0x63, 0xc3,
+    0x0c, 0x4f, 0x1d, 0x5e, 0x43, 0x65, 0x33, 0x36,
+    0xfe, 0xf3, 0x3b, 0x10, 0x3c, 0x67, 0xe7, 0xa9,
+    0x8a, 0xdd, 0x2d, 0x3b, 0x66, 0xe2, 0xfd, 0xa9,
+    0x5b, 0x5b, 0x2a, 0x66, 0x7a, 0xa9, 0xda, 0xc7,
+    0xe5, 0x9c, 0xc1, 0xd4, 0x6d, 0x30, 0xe8, 0x18
+};
+
+static const unsigned char p521_auth_ikme[] = {
+    0xfe, 0x1c, 0x58, 0x9c, 0x2a, 0x05, 0x89, 0x38,
+    0x95, 0xa5, 0x37, 0xf3, 0x8c, 0x7c, 0xb4, 0x30,
+    0x0b, 0x5a, 0x7e, 0x8f, 0xef, 0x3d, 0x6c, 0xcb,
+    0x8f, 0x07, 0xa4, 0x98, 0x02, 0x9c, 0x61, 0xe9,
+    0x02, 0x62, 0xe0, 0x09, 0xdc, 0x25, 0x4c, 0x7f,
+    0x62, 0x35, 0xf9, 0xc6, 0xb2, 0xfd, 0x6a, 0xef,
+    0xf0, 0xa7, 0x14, 0xdb, 0x13, 0x1b, 0x09, 0x25,
+    0x8c, 0x16, 0xe2, 0x17, 0xb7, 0xbd, 0x2a, 0xa6,
+    0x19, 0xb0
+};
+
+static const unsigned char p521_auth_ikmr_pub[] = {
+    0x04, 0x00, 0x7d, 0x41, 0x9b, 0x88, 0x34, 0xe7,
+    0x51, 0x3d, 0x0e, 0x7c, 0xc6, 0x64, 0x24, 0xa1,
+    0x36, 0xec, 0x5e, 0x11, 0x39, 0x5a, 0xb3, 0x53,
+    0xda, 0x32, 0x4e, 0x35, 0x86, 0x67, 0x3e, 0xe7,
+    0x3d, 0x53, 0xab, 0x34, 0xf3, 0x0a, 0x0b, 0x42,
+    0xa9, 0x2d, 0x05, 0x4d, 0x0d, 0xb3, 0x21, 0xb8,
+    0x0f, 0x62, 0x17, 0xe6, 0x55, 0xe3, 0x04, 0xf7,
+    0x27, 0x93, 0x76, 0x7c, 0x42, 0x31, 0x78, 0x5c,
+    0x4a, 0x4a, 0x6e, 0x00, 0x8f, 0x31, 0xb9, 0x3b,
+    0x7a, 0x4f, 0x2b, 0x8c, 0xd1, 0x2e, 0x5f, 0xe5,
+    0xa0, 0x52, 0x3d, 0xc7, 0x13, 0x53, 0xc6, 0x6c,
+    0xbd, 0xad, 0x51, 0xc8, 0x6b, 0x9e, 0x0b, 0xdf,
+    0xcd, 0x9a, 0x45, 0x69, 0x8f, 0x2d, 0xab, 0x18,
+    0x09, 0xab, 0x1b, 0x0f, 0x88, 0xf5, 0x42, 0x27,
+    0x23, 0x2c, 0x85, 0x8a, 0xcc, 0xc4, 0x4d, 0x9a,
+    0x8d, 0x41, 0x77, 0x5a, 0xc0, 0x26, 0x34, 0x15,
+    0x64, 0xa2, 0xd7, 0x49, 0xf4
+};
+
+static const unsigned char p521_auth_ikmr_priv[] = {
+    0x01, 0x3e, 0xf3, 0x26, 0x94, 0x09, 0x98, 0x54,
+    0x4a, 0x89, 0x9e, 0x15, 0xe1, 0x72, 0x65, 0x48,
+    0xff, 0x43, 0xbb, 0xdb, 0x23, 0xa8, 0x58, 0x7a,
+    0xa3, 0xbe, 0xf9, 0xd1, 0xb8, 0x57, 0x33, 0x8d,
+    0x87, 0x28, 0x7d, 0xf5, 0x66, 0x70, 0x37, 0xb5,
+    0x19, 0xd6, 0xa1, 0x46, 0x61, 0xe9, 0x50, 0x3c,
+    0xfc, 0x95, 0xa1, 0x54, 0xd9, 0x35, 0x66, 0xd8,
+    0xc8, 0x4e, 0x95, 0xce, 0x93, 0xad, 0x05, 0x29,
+    0x3a, 0x0b
+};
+
+static const unsigned char p521_auth_ikms_pub[] = {
+    0x04, 0x01, 0x5c, 0xc3, 0x63, 0x66, 0x32, 0xea,
+    0x9a, 0x38, 0x79, 0xe4, 0x32, 0x40, 0xbe, 0xae,
+    0x5d, 0x15, 0xa4, 0x4f, 0xba, 0x81, 0x92, 0x82,
+    0xfa, 0xc2, 0x6a, 0x19, 0xc9, 0x89, 0xfa, 0xfd,
+    0xd0, 0xf3, 0x30, 0xb8, 0x52, 0x1d, 0xff, 0x7d,
+    0xc3, 0x93, 0x10, 0x1b, 0x01, 0x8c, 0x1e, 0x65,
+    0xb0, 0x7b, 0xe9, 0xf5, 0xfc, 0x9a, 0x28, 0xa1,
+    0xf4, 0x50, 0xd6, 0xa5, 0x41, 0xee, 0x0d, 0x76,
+    0x22, 0x11, 0x33, 0x00, 0x1e, 0x8f, 0x0f, 0x6a,
+    0x05, 0xab, 0x79, 0xf9, 0xb9, 0xbb, 0x9c, 0xcc,
+    0xe1, 0x42, 0xa4, 0x53, 0xd5, 0x9c, 0x5a, 0xbe,
+    0xbb, 0x56, 0x74, 0x83, 0x9d, 0x93, 0x5a, 0x3c,
+    0xa1, 0xa3, 0xfb, 0xc3, 0x28, 0x53, 0x9a, 0x60,
+    0xb3, 0xbc, 0x3c, 0x05, 0xfe, 0xd2, 0x28, 0x38,
+    0x58, 0x4a, 0x72, 0x6b, 0x9c, 0x17, 0x67, 0x96,
+    0xca, 0xd0, 0x16, 0x9b, 0xa4, 0x09, 0x33, 0x32,
+    0xcb, 0xd2, 0xdc, 0x3a, 0x9f
+};
+
+static const unsigned char p521_auth_ikms_priv[] = {
+    0x00, 0x10, 0x18, 0x58, 0x45, 0x99, 0x62, 0x5f,
+    0xf9, 0x95, 0x3b, 0x93, 0x05, 0x84, 0x98, 0x50,
+    0xd5, 0xe3, 0x4b, 0xd7, 0x89, 0xd4, 0xb8, 0x11,
+    0x01, 0x13, 0x96, 0x62, 0xfb, 0xea, 0x8b, 0x65,
+    0x08, 0xdd, 0xb9, 0xd0, 0x19, 0xb0, 0xd6, 0x92,
+    0xe7, 0x37, 0xf6, 0x6b, 0xea, 0xe3, 0xf1, 0xf7,
+    0x83, 0xe7, 0x44, 0x20, 0x2a, 0xaf, 0x6f, 0xea,
+    0x01, 0x50, 0x6c, 0x27, 0x28, 0x7e, 0x35, 0x9f,
+    0xe7, 0x76
+};
+
+static const unsigned char p521_auth_expected_enc[] = {
+    0x04, 0x01, 0x7d, 0xe1, 0x2e, 0xde, 0x7f, 0x72,
+    0xcb, 0x10, 0x1d, 0xab, 0x36, 0xa1, 0x11, 0x26,
+    0x5c, 0x97, 0xb3, 0x65, 0x48, 0x16, 0xdc, 0xd6,
+    0x18, 0x3f, 0x80, 0x9d, 0x4b, 0x3d, 0x11, 0x1f,
+    0xe7, 0x59, 0x49, 0x7f, 0x8a, 0xef, 0xdc, 0x5d,
+    0xbb, 0x40, 0xd3, 0xe6, 0xd2, 0x1d, 0xb1, 0x5b,
+    0xdc, 0x60, 0xf1, 0x5f, 0x2a, 0x42, 0x07, 0x61,
+    0xbc, 0xae, 0xef, 0x73, 0xb8, 0x91, 0xc2, 0xb1,
+    0x17, 0xe9, 0xcf, 0x01, 0xe2, 0x93, 0x20, 0xb7,
+    0x99, 0xbb, 0xc8, 0x6a, 0xfd, 0xc5, 0xea, 0x97,
+    0xd9, 0x41, 0xea, 0x1c, 0x5b, 0xd5, 0xeb, 0xee,
+    0xac, 0x7a, 0x78, 0x4b, 0x3b, 0xab, 0x52, 0x47,
+    0x46, 0xf3, 0xe6, 0x40, 0xec, 0x26, 0xee, 0x1b,
+    0xd9, 0x12, 0x55, 0xf9, 0x33, 0x0d, 0x97, 0x4f,
+    0x84, 0x50, 0x84, 0x63, 0x7e, 0xe0, 0xe6, 0xfe,
+    0x9f, 0x50, 0x5c, 0x5b, 0x87, 0xc8, 0x6a, 0x4e,
+    0x1a, 0x6c, 0x30, 0x96, 0xdd
+};
+
+static const unsigned char p521_auth_expected_secret[] = {
+    0x26, 0x64, 0x8f, 0xa2, 0xa2, 0xde, 0xb0, 0xbf,
+    0xc5, 0x63, 0x49, 0xa5, 0x90, 0xfd, 0x4c, 0xb7,
+    0x10, 0x8a, 0x51, 0x79, 0x7b, 0x63, 0x46, 0x94,
+    0xfc, 0x02, 0x06, 0x1e, 0x8d, 0x91, 0xb3, 0x57,
+    0x6a, 0xc7, 0x36, 0xa6, 0x8b, 0xf8, 0x48, 0xfe,
+    0x2a, 0x58, 0xdf, 0xb1, 0x95, 0x6d, 0x26, 0x6e,
+    0x68, 0x20, 0x9a, 0x4d, 0x63, 0x1e, 0x51, 0x3b,
+    0xad, 0xf8, 0xf4, 0xdc, 0xfc, 0x00, 0xf3, 0x0a
+};
+
+static const TEST_DERIVEKEY_DATA ec_derivekey_data[] = {
+    {
+      "P-256",
+      p256_ikme, sizeof(p256_ikme),
+      p256_ikme_pub, sizeof(p256_ikme_pub),
+      p256_ikme_priv, sizeof(p256_ikme_priv)
+    },
+    {
+      "P-256",
+      p256_ikmr, sizeof(p256_ikmr),
+      p256_ikmr_pub, sizeof(p256_ikmr_pub),
+      p256_ikmr_priv, sizeof(p256_ikmr_priv)
+    },
+    {
+      "P-521",
+      p521_ikme, sizeof(p521_ikme),
+      p521_ikme_pub, sizeof(p521_ikme_pub),
+      p521_ikme_priv, sizeof(p521_ikme_priv)
+    }
+};
+
+static const TEST_ENCAPDATA ec_encapdata[] = {
+    {
+        "P-256",
+        p256_ikme, sizeof(p256_ikme),
+        p256_ikmr_pub, sizeof(p256_ikmr_pub),
+        p256_ikmr_priv, sizeof(p256_ikmr_priv),
+        p256_expected_enc, sizeof(p256_expected_enc),
+        p256_expected_secret, sizeof(p256_expected_secret),
+    },
+    {
+        "X25519",
+        x25519_ikme, sizeof(x25519_ikme),
+        x25519_rpub, sizeof(x25519_rpub),
+        x25519_rpriv, sizeof(x25519_rpriv),
+        x25519_expected_enc, sizeof(x25519_expected_enc),
+        x25519_expected_secret, sizeof(x25519_expected_secret),
+    },
+    {
+        "P-521",
+        p521_ikme, sizeof(p521_ikme),
+        p521_ikmr_pub, sizeof(p521_ikmr_pub),
+        p521_ikmr_priv, sizeof(p521_ikmr_priv),
+        p521_expected_enc, sizeof(p521_expected_enc),
+        p521_expected_secret, sizeof(p521_expected_secret),
+    },
+    {
+        "P-521",
+        p521_auth_ikme, sizeof(p521_auth_ikme),
+        p521_auth_ikmr_pub, sizeof(p521_auth_ikmr_pub),
+        p521_auth_ikmr_priv, sizeof(p521_auth_ikmr_priv),
+        p521_auth_expected_enc, sizeof(p521_auth_expected_enc),
+        p521_auth_expected_secret, sizeof(p521_auth_expected_secret),
+        p521_auth_ikms_pub, sizeof(p521_auth_ikms_pub),
+        p521_auth_ikms_priv, sizeof(p521_auth_ikms_priv)
+    },
+    {
+        "X25519",
+        x25519_auth_ikme, sizeof(x25519_auth_ikme),
+        x25519_auth_rpub, sizeof(x25519_auth_rpub),
+        x25519_auth_rpriv, sizeof(x25519_auth_rpriv),
+        x25519_auth_expected_enc, sizeof(x25519_auth_expected_enc),
+        x25519_auth_expected_secret, sizeof(x25519_auth_expected_secret),
+        x25519_auth_spub, sizeof(x25519_auth_spub),
+        x25519_auth_spriv, sizeof(x25519_auth_spriv)
+    }
+};
+
+/* Test vector from https://github.com/cfrg/draft-irtf-cfrg-hpke */
+static const unsigned char x448_ikmr[] = {
+    0xd4, 0x5d, 0x16, 0x52, 0xdf, 0x74, 0x92, 0x0a,
+    0xbf, 0x94, 0xa2, 0x88, 0x3c, 0x83, 0x05, 0x0f,
+    0x50, 0x2f, 0xf5, 0x12, 0xff, 0xb5, 0x6f, 0x07,
+    0xb6, 0xd8, 0x33, 0xec, 0x8d, 0xda, 0x74, 0xb6,
+    0xa1, 0xc1, 0xcc, 0x4d, 0x42, 0xa2, 0x26, 0x41,
+    0xc0, 0x96, 0x3d, 0x3c, 0x21, 0xed, 0x82, 0x61,
+    0xf3, 0x44, 0xdc, 0x9e, 0x05, 0x01, 0xa8, 0x1c
+};
+static const unsigned char x448_ikmr_priv[] = {
+    0x27, 0xa4, 0x35, 0x46, 0x08, 0xf3, 0xbd, 0xd3,
+    0x8f, 0x1f, 0x5a, 0xf3, 0x05, 0xf3, 0xe0, 0x68,
+    0x2e, 0xfe, 0x4e, 0x25, 0x80, 0x82, 0x49, 0xd8,
+    0xfc, 0xb5, 0x59, 0x27, 0xf6, 0xa9, 0xf4, 0x46,
+    0xb8, 0xdc, 0x1d, 0x0a, 0x2c, 0x3b, 0x8c, 0xb1,
+    0x33, 0xa5, 0x67, 0x3b, 0x59, 0xa6, 0xd5, 0x5c,
+    0xe7, 0x54, 0xec, 0x0c, 0x9a, 0x55, 0x54, 0x01
+};
+static const unsigned char x448_ikmr_pub[] = {
+    0x14, 0x5d, 0x08, 0x3e, 0xa7, 0xa6, 0x37, 0x9d,
+    0xbb, 0x32, 0xdc, 0xbd, 0x8a, 0xff, 0x4c, 0x20,
+    0x6e, 0xa5, 0xd0, 0x69, 0xb7, 0x5e, 0x96, 0xc6,
+    0xdd, 0x2a, 0x3e, 0x38, 0xf4, 0x41, 0x47, 0x1a,
+    0xc9, 0x7a, 0xdc, 0xa6, 0x41, 0xfd, 0xad, 0x66,
+    0x68, 0x5a, 0x96, 0xf3, 0x2b, 0x7c, 0x3e, 0x06,
+    0x46, 0x35, 0xfa, 0xb3, 0xcc, 0x89, 0x23, 0x4e
+};
+
+static const TEST_DERIVEKEY_DATA ecx_derivekey_data[] = {
+    {
+      "X25519",
+      x25519_ikme, sizeof(x25519_ikme),
+      x25519_ikme_pub, sizeof(x25519_ikme_pub),
+      x25519_ikme_priv, sizeof(x25519_ikme_priv)
+    },
+    {
+      "X448",
+      x448_ikmr, sizeof(x448_ikmr),
+      x448_ikmr_pub, sizeof(x448_ikmr_pub),
+      x448_ikmr_priv, sizeof(x448_ikmr_priv)
+    },
+};
+
+/*
+ * Helper function to create a EC or ECX private key from bytes.
+ * The public key can optionally be NULL.
+ */
+static EVP_PKEY *new_raw_private_key(const char *curvename,
+                                     const unsigned char *priv, size_t privlen,
+                                     const unsigned char *pub, size_t publen)
+{
+    int ok = 0;
+    EVP_PKEY_CTX *ctx;
+    EVP_PKEY *key = NULL;
+    OSSL_PARAM *params = NULL;
+    BIGNUM *privbn = NULL;
+    OSSL_PARAM_BLD *bld = NULL;
+    int ecx = (curvename[0] == 'X');
+
+    if (ecx)
+        ctx = EVP_PKEY_CTX_new_from_name(libctx, curvename, NULL);
+    else
+        ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL);
+    if (ctx == NULL)
+        return 0;
+
+    bld = OSSL_PARAM_BLD_new();
+    if (bld == NULL)
+        goto err;
+
+    if (ecx) {
+        if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PRIV_KEY,
+                                             (char *)priv, privlen))
+            goto err;
+    } else {
+        privbn = BN_bin2bn(priv, privlen, NULL);
+        if (privbn == NULL)
+            goto err;
+        if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME,
+                                             curvename, 0))
+            goto err;
+        if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, privbn))
+            goto err;
+    }
+
+    if (pub != NULL) {
+        if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY,
+                                             (char *)pub, publen))
+            goto err;
+    }
+    params = OSSL_PARAM_BLD_to_param(bld);
+    if (params == NULL)
+        goto err;
+
+    if (EVP_PKEY_fromdata_init(ctx) <= 0)
+          goto err;
+    if (EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_KEYPAIR, params) <= 0)
+          goto err;
+    ok = 1;
+err:
+    if (!ok) {
+        EVP_PKEY_free(key);
+        key = NULL;
+    }
+    BN_free(privbn);
+    OSSL_PARAM_free(params);
+    OSSL_PARAM_BLD_free(bld);
+    EVP_PKEY_CTX_free(ctx);
+    return key;
+}
+
+static EVP_PKEY *new_raw_public_key(const char *curvename,
+                                    const unsigned char *pub, size_t publen)
+{
+    int ok = 0;
+    EVP_PKEY_CTX *ctx;
+    EVP_PKEY *key = NULL;
+    OSSL_PARAM params[3], *p = params;
+    int ecx = (curvename[0] == 'X');
+
+    if (ecx)
+        ctx = EVP_PKEY_CTX_new_from_name(libctx, curvename, NULL);
+    else
+        ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL);
+    if (ctx == NULL)
+        return 0;
+
+    if (!ecx)
+        *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                (char *)curvename, 0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+                                             (char *)pub, publen);
+    *p = OSSL_PARAM_construct_end();
+    if (EVP_PKEY_fromdata_init(ctx) <= 0)
+          goto err;
+    if (EVP_PKEY_fromdata(ctx, &key, EVP_PKEY_PUBLIC_KEY, params) <= 0)
+          goto err;
+    ok = 1;
+err:
+    if (!ok) {
+        EVP_PKEY_free(key);
+        key = NULL;
+    }
+    EVP_PKEY_CTX_free(ctx);
+    return key;
+}
+
+/* Helper function to perform encapsulation */
+static int do_encap(const TEST_ENCAPDATA *t, EVP_PKEY *rpub, EVP_PKEY *spriv)
+{
+    int ret = 0;
+    unsigned char secret[256] = { 0, };
+    unsigned char enc[256] = { 0, };
+    size_t secretlen = 0, enclen = 0;
+    EVP_PKEY_CTX *sctx = NULL;
+    OSSL_PARAM params[3], *p = params;
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION,
+                                            (char *)OSSL_KEM_PARAM_OPERATION_DHKEM,
+                                            0);
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_KEM_PARAM_IKME,
+                                             (char *)t->ikmE, t->ikmElen);
+    *p = OSSL_PARAM_construct_end();
+
+    if (!TEST_ptr(sctx = EVP_PKEY_CTX_new_from_pkey(libctx, rpub, NULL)))
+        goto err;
+    if (t->spriv == NULL) {
+        if (!TEST_int_eq(EVP_PKEY_encapsulate_init(sctx, params), 1))
+            goto err;
+    } else {
+        if (!TEST_int_eq(EVP_PKEY_auth_encapsulate_init(sctx, spriv, params), 1))
+        goto err;
+    }
+    ret = TEST_int_eq(EVP_PKEY_encapsulate(sctx, NULL, &enclen, NULL,
+                                              &secretlen), 1)
+          && TEST_int_eq(EVP_PKEY_encapsulate(sctx, enc, &enclen, secret,
+                                              &secretlen), 1)
+          && TEST_mem_eq(enc, enclen, t->expected_enc, t->expected_enclen)
+          && TEST_mem_eq(secret, secretlen,
+                         t->expected_secret, t->expected_secretlen);
+err:
+    EVP_PKEY_CTX_free(sctx);
+    return ret;
+}
+
+/* Helper function to perform decapsulation */
+static int do_decap(const TEST_ENCAPDATA *t, EVP_PKEY *rpriv, EVP_PKEY *spub)
+{
+    int ret = 0;
+    EVP_PKEY_CTX *recipctx = NULL;
+    unsigned char secret[256] = { 0, };
+    size_t secretlen = 0;
+
+    if (!TEST_ptr(recipctx = EVP_PKEY_CTX_new_from_pkey(libctx, rpriv, NULL)))
+        goto err;
+    if (t->spub == NULL) {
+        if (!TEST_int_eq(EVP_PKEY_decapsulate_init(recipctx, opparam), 1))
+            goto err;
+    } else {
+        if (!TEST_int_eq(EVP_PKEY_auth_decapsulate_init(recipctx, spub,
+                                                        opparam), 1))
+            goto err;
+    }
+    ret = TEST_int_eq(EVP_PKEY_decapsulate(recipctx, NULL, &secretlen,
+                                              t->expected_enc,
+                                              t->expected_enclen), 1)
+          && TEST_int_eq(EVP_PKEY_decapsulate(recipctx, secret, &secretlen,
+                                              t->expected_enc,
+                                              t->expected_enclen), 1)
+          && TEST_mem_eq(secret, secretlen,
+                         t->expected_secret, t->expected_secretlen);
+err:
+    EVP_PKEY_CTX_free(recipctx);
+    return ret;
+}
diff --git a/test/evp_pkey_dhkem_test.c b/test/evp_pkey_dhkem_test.c
new file mode 100644 (file)
index 0000000..95fcf8f
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/evp.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include <openssl/proverr.h>
+#include "internal/nelem.h"
+#include "testutil.h"
+
+#define TEST_KEM_ENCAP       0
+#define TEST_KEM_DECAP       1
+#define TEST_KEM_ENCAP_DECAP 2
+
+#define TEST_TYPE_AUTH        0
+#define TEST_TYPE_NOAUTH      1
+#define TEST_TYPE_AUTH_NOAUTH 2
+
+#define TEST_KEYTYPE_P256         0
+#define TEST_KEYTYPE_X25519       1
+#define TEST_KEYTYPES_P256_X25519 2
+
+static OSSL_LIB_CTX *libctx = NULL;
+static OSSL_PROVIDER *nullprov = NULL;
+static OSSL_PROVIDER *libprov = NULL;
+static OSSL_PARAM opparam[2];
+static EVP_PKEY *rkey[TEST_KEYTYPES_P256_X25519] = { NULL, NULL };
+static EVP_PKEY_CTX *rctx[TEST_KEYTYPES_P256_X25519] = { NULL, NULL };
+
+#include "dhkem_test.inc"
+
+/* Perform encapsulate KAT's */
+static int test_dhkem_encapsulate(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY *rpub = NULL, *spriv = NULL;
+    const TEST_ENCAPDATA *t = &ec_encapdata[tstid];
+
+    TEST_note("Test %s %s Decapsulate", t->curve,
+              t->spriv != NULL ? "Auth" : "");
+
+    if (!TEST_ptr(rpub = new_raw_public_key(t->curve, t->rpub, t->rpublen)))
+        goto err;
+
+    if (t->spriv != NULL) {
+        if (!TEST_ptr(spriv = new_raw_private_key(t->curve,
+                                                  t->spriv, t->sprivlen,
+                                                  t->spub, t->spublen)))
+            goto err;
+    }
+    ret = do_encap(t, rpub, spriv);
+err:
+    EVP_PKEY_free(spriv);
+    EVP_PKEY_free(rpub);
+    return ret;
+}
+
+/* Perform decapsulate KAT's */
+static int test_dhkem_decapsulate(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY *rpriv = NULL, *spub = NULL;
+    const TEST_ENCAPDATA *t = &ec_encapdata[tstid];
+
+    TEST_note("Test %s %s Decapsulate", t->curve, t->spub != NULL ? "Auth" : "");
+
+    if (!TEST_ptr(rpriv = new_raw_private_key(t->curve, t->rpriv, t->rprivlen,
+                                              t->rpub, t->rpublen)))
+        goto err;
+    if (t->spub != NULL) {
+        if (!TEST_ptr(spub = new_raw_public_key(t->curve, t->spub, t->spublen)))
+            goto err;
+    }
+    ret = do_decap(t, rpriv, spub);
+err:
+    EVP_PKEY_free(spub);
+    EVP_PKEY_free(rpriv);
+    return ret;
+}
+
+/* Test that there are settables and they have correct data types */
+static int test_settables(int tstid)
+{
+    EVP_PKEY_CTX *ctx = rctx[tstid];
+    const OSSL_PARAM *settableparams;
+    const OSSL_PARAM *p;
+
+    return TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), 1)
+           && TEST_ptr(settableparams = EVP_PKEY_CTX_settable_params(ctx))
+           && TEST_ptr(p = OSSL_PARAM_locate_const(settableparams,
+                                                   OSSL_KEM_PARAM_OPERATION))
+           && TEST_uint_eq(p->data_type, OSSL_PARAM_UTF8_STRING)
+           && TEST_ptr(p = OSSL_PARAM_locate_const(settableparams,
+                                                   OSSL_KEM_PARAM_IKME))
+          && TEST_uint_eq(p->data_type, OSSL_PARAM_OCTET_STRING);
+}
+
+/* Test initing multiple times passes */
+static int test_init_multiple(int tstid)
+{
+    EVP_PKEY_CTX *ctx = rctx[tstid];
+
+    return TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), 1)
+           && TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), 1)
+           && TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, NULL), 1)
+           && TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, NULL), 1);
+}
+
+/* Fail is various bad inputs are passed to the derivekey (keygen) operation */
+static int test_ec_dhkem_derivekey_fail(void)
+{
+    int ret = 0;
+    EVP_PKEY *pkey = NULL;
+    OSSL_PARAM params[3];
+    EVP_PKEY_CTX *genctx = NULL;
+    const TEST_DERIVEKEY_DATA *t = &ec_derivekey_data[0];
+    BIGNUM *priv = NULL;
+
+    /* Check non nist curve fails */
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                 "secp256k1", 0);
+    params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                  (char *)t->ikm, t->ikmlen);
+    params[2] = OSSL_PARAM_construct_end();
+
+    if (!TEST_ptr(genctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL))
+        || !TEST_int_eq(EVP_PKEY_keygen_init(genctx), 1)
+        || !TEST_int_eq(EVP_PKEY_CTX_set_params(genctx, params), 1)
+        || !TEST_int_eq(EVP_PKEY_generate(genctx, &pkey),0))
+        goto err;
+
+    /* Fail if curve is not one of P-256, P-384 or P-521 */
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                 "P-224", 0);
+    params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                  (char *)t->ikm, t->ikmlen);
+    params[2] = OSSL_PARAM_construct_end();
+    if (!TEST_int_eq(EVP_PKEY_keygen_init(genctx), 1)
+        || !TEST_int_eq(EVP_PKEY_CTX_set_params(genctx, params), 1)
+        || !TEST_int_eq(EVP_PKEY_generate(genctx, &pkey), 0))
+        goto err;
+
+    /* Fail if ikm len is too small*/
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                 "P-256", 0);
+    params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                  (char *)t->ikm, t->ikmlen - 1);
+    params[2] = OSSL_PARAM_construct_end();
+    if (!TEST_int_eq(EVP_PKEY_CTX_set_params(genctx, params), 1)
+        || !TEST_int_eq(EVP_PKEY_generate(genctx, &pkey), 0))
+        goto err;
+
+    ret = 1;
+err:
+    BN_free(priv);
+    EVP_PKEY_free(pkey);
+    EVP_PKEY_CTX_free(genctx);
+    return ret;
+}
+
+/* Fail if the operation parameter is not set */
+static int test_no_operation_set(int tstid)
+{
+    EVP_PKEY_CTX *ctx = rctx[tstid];
+    const TEST_ENCAPDATA *t = &ec_encapdata[tstid];
+    size_t len = 0;
+
+    return TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), 1)
+           && TEST_int_eq(EVP_PKEY_encapsulate(ctx, NULL, &len, NULL, NULL), -2)
+           && TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, NULL), 1)
+           && TEST_int_eq(EVP_PKEY_decapsulate(ctx, NULL, &len,
+                                               t->expected_enc,
+                                               t->expected_enclen), -2);
+}
+
+/* Fail if the ikm is too small */
+static int test_ikm_small(int tstid)
+{
+    unsigned char tmp[16] = { 0 };
+    unsigned char secret[256];
+    unsigned char enc[256];
+    size_t secretlen = sizeof(secret);
+    size_t enclen = sizeof(enc);
+    OSSL_PARAM params[3];
+    EVP_PKEY_CTX *ctx = rctx[tstid];
+
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION,
+                                                 OSSL_KEM_PARAM_OPERATION_DHKEM,
+                                                 0);
+    params[1] = OSSL_PARAM_construct_octet_string(OSSL_KEM_PARAM_IKME,
+                                                  tmp, sizeof(tmp));
+    params[2] = OSSL_PARAM_construct_end();
+
+    return TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, params), 1)
+           && TEST_int_eq(EVP_PKEY_encapsulate(ctx, enc, &enclen,
+                                               secret, &secretlen), 0);
+}
+
+/* Fail if buffers lengths are too small to hold returned data */
+static int test_input_size_small(int tstid)
+{
+    int ret = 0;
+    unsigned char sec[256];
+    unsigned char enc[256];
+    size_t seclen = sizeof(sec);
+    size_t enclen = sizeof(enc);
+    EVP_PKEY_CTX *ctx = rctx[tstid];
+
+    if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, opparam), 1)
+        || !TEST_int_eq(EVP_PKEY_encapsulate(ctx, NULL, &enclen,
+                                             NULL, &seclen), 1))
+    goto err;
+
+    /* buffer too small for enc */
+    enclen--;
+    if (!TEST_int_eq(EVP_PKEY_encapsulate(ctx, enc, &enclen, sec, &seclen),
+                     0))
+        goto err;
+    enclen++;
+    /* buffer too small for secret */
+    seclen--;
+    if (!TEST_int_eq(EVP_PKEY_encapsulate(ctx, enc, &enclen, sec, &seclen), 0))
+        goto err;
+    seclen++;
+    if (!TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, opparam), 1))
+        goto err;
+     /* buffer too small for decapsulate secret */
+    seclen--;
+    if (!TEST_int_eq(EVP_PKEY_decapsulate(ctx, sec, &seclen, enc, enclen), 0))
+        goto err;
+    seclen++;
+     /* incorrect enclen passed to decap  */
+    enclen--;
+    ret = TEST_int_eq(EVP_PKEY_decapsulate(ctx, sec, &seclen, enc, enclen), 0);
+err:
+    return ret;
+}
+
+/* Fail if the auth key has a different curve */
+static int test_ec_auth_key_curve_mismatch(void)
+{
+    int ret = 0;
+    EVP_PKEY *auth = NULL;
+
+    if (!TEST_ptr(auth = EVP_PKEY_Q_keygen(libctx, NULL, "EC", "P-521")))
+        return 0;
+
+    ret = TEST_int_eq(EVP_PKEY_auth_encapsulate_init(rctx[0], auth, opparam), 0);
+    EVP_PKEY_free(auth);
+    return ret;
+}
+
+/* Fail if the auth key has a different key type to the recipient */
+static int test_auth_key_type_mismatch(int tstid)
+{
+    int id1 = tstid;
+    int id2 = !tstid;
+
+    return TEST_int_eq(EVP_PKEY_auth_encapsulate_init(rctx[id1],
+                                                      rkey[id2], opparam), 0);
+}
+
+static int test_ec_invalid_private_key(void)
+{
+    int ret = 0;
+    EVP_PKEY *priv = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+    const TEST_ENCAPDATA *t = &ec_encapdata[0];
+    static const unsigned char order[] = {
+        0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84,
+        0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51
+    };
+
+    ret = TEST_ptr(priv = new_raw_private_key("P-256", order, sizeof(order),
+                                              t->rpub, t->rpublen))
+          && TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, priv, NULL))
+          && TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), 0);
+    EVP_PKEY_free(priv);
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
+static int test_ec_public_key_infinity(void)
+{
+    int ret = 0;
+    EVP_PKEY *key = NULL;
+    EVP_PKEY_CTX *keyctx = NULL;
+    unsigned char s[256];
+    unsigned char e[256];
+    size_t slen = sizeof(s);
+    size_t elen = sizeof(e);
+    unsigned char tmp[1] = { 0 }; /* The encoding for an EC point at infinity */
+    EVP_PKEY_CTX *ctx = rctx[0];
+    const TEST_ENCAPDATA *t = &ec_encapdata[0];
+
+    ret = TEST_ptr(key = new_raw_private_key(t->curve, t->rpriv, t->rprivlen,
+                                                tmp, sizeof(tmp)))
+          && TEST_ptr(keyctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL))
+          /* Fail if the recipient public key is invalid */
+          && TEST_int_eq(EVP_PKEY_encapsulate_init(keyctx, opparam), 1)
+          && TEST_int_eq(EVP_PKEY_encapsulate(keyctx, e, &elen, s, &slen), 0)
+          /* Fail the decap if the recipient public key is invalid */
+          && TEST_int_eq(EVP_PKEY_decapsulate_init(keyctx, opparam), 1)
+          && TEST_int_eq(EVP_PKEY_decapsulate(keyctx, s, &slen,
+                                              t->expected_enc,
+                                              t->expected_enclen), 0)
+          /* Fail if the auth key has a bad public key */
+          && TEST_int_eq(EVP_PKEY_auth_encapsulate_init(ctx, key, opparam), 1)
+          && TEST_int_eq(EVP_PKEY_encapsulate(ctx, e, &elen, s, &slen), 0);
+
+    EVP_PKEY_free(key);
+    EVP_PKEY_CTX_free(keyctx);
+    return ret;
+}
+
+/* Test incorrectly passing NULL values fail */
+static int test_null_params(int tstid)
+{
+    EVP_PKEY_CTX *ctx = rctx[tstid];
+    const TEST_ENCAPDATA *t = &ec_encapdata[tstid];
+
+    /* auth_encap/decap init must be passed a non NULL value */
+    return TEST_int_eq(EVP_PKEY_auth_encapsulate_init(ctx, NULL, opparam), 0)
+           && TEST_int_eq(EVP_PKEY_auth_decapsulate_init(ctx, NULL, opparam), 0)
+           /* Check decap fails if NULL params are passed */
+           && TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, opparam), 1)
+           && TEST_int_eq(EVP_PKEY_decapsulate(ctx, NULL, NULL,
+                                               t->expected_enc,
+                                               t->expected_enclen), 0)
+           /* Check encap fails if NULL params are passed */
+           && TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, opparam), 1)
+           && TEST_int_eq(EVP_PKEY_encapsulate(ctx, NULL, NULL,
+                                               NULL, NULL), 0);
+}
+
+static int test_set_params(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY_CTX *ctx = rctx[tstid];
+    OSSL_PARAM badparams[4];
+    int val = 1;
+
+    /* wrong data type for operation param */
+    badparams[0] = OSSL_PARAM_construct_int(OSSL_KEM_PARAM_OPERATION, &val);
+    badparams[1] = OSSL_PARAM_construct_end();
+    if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, badparams), 0))
+        goto err;
+    /* unknown string used for the operation param */
+    badparams[0] = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION,
+                                                    "unknown_op", 0);
+    badparams[1] = OSSL_PARAM_construct_end();
+    if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, badparams), 0))
+        goto err;
+
+    /* NULL string set for the operation param */
+    badparams[0] = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION,
+                                                    NULL, 0);
+    badparams[1] = OSSL_PARAM_construct_end();
+    if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, badparams), 0))
+        goto err;
+
+    /* wrong data type for ikme param */
+    badparams[0] = OSSL_PARAM_construct_int(OSSL_KEM_PARAM_IKME, &val);
+    badparams[1] = OSSL_PARAM_construct_end();
+    if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, badparams), 0))
+        goto err;
+
+    /* Setting the ikme to NULL is allowed */
+    badparams[0] = OSSL_PARAM_construct_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0);
+    badparams[1] = OSSL_PARAM_construct_end();
+    if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, badparams), 1))
+        goto err;
+
+    /* Test that unknown params are ignored */
+    badparams[0] = OSSL_PARAM_construct_int("unknownparam", &val);
+    badparams[1] = OSSL_PARAM_construct_end();
+    ret = TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, badparams), 1);
+err:
+    return ret;
+}
+
+/*
+ * ECX keys autogen the public key if a private key is loaded,
+ * So this test passes for ECX, but fails for EC
+ */
+static int test_nopublic(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_PKEY *priv = NULL;
+    int encap = ((tstid & 1) == 0);
+    int keytype = tstid >= TEST_KEM_ENCAP_DECAP;
+    const TEST_ENCAPDATA *t = &ec_encapdata[keytype];
+    int expected = (keytype == TEST_KEYTYPE_X25519);
+
+    TEST_note("%s %s", t->curve, encap ? "Encap" : "Decap");
+    if (!TEST_ptr(priv = new_raw_private_key(t->curve, t->rpriv, t->rprivlen,
+                                             NULL, 0)))
+        goto err;
+    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, priv, NULL)))
+        goto err;
+
+    if (encap) {
+        if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, opparam), expected))
+            goto err;
+    } else {
+        if (!TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, opparam), expected))
+        goto err;
+    }
+    if (expected == 0
+        && !TEST_int_eq(ERR_GET_REASON(ERR_get_error()), PROV_R_NOT_A_PUBLIC_KEY))
+        goto err;
+    ret = 1;
+err:
+    EVP_PKEY_free(priv);
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
+/* Test that not setting the auth public key fails the auth encap/decap init */
+static int test_noauthpublic(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY *auth = NULL;
+    int encap = ((tstid & 1) == 0);
+    int keytype = tstid >= TEST_KEM_ENCAP_DECAP;
+    const TEST_ENCAPDATA *t = &ec_encapdata[keytype];
+    EVP_PKEY_CTX *ctx = rctx[keytype];
+    int expected = (keytype == TEST_KEYTYPE_X25519);
+
+    TEST_note("%s %s", t->curve, encap ? "Encap" : "Decap");
+    if (!TEST_ptr(auth = new_raw_private_key(t->curve, t->rpriv,
+                                             t->rprivlen, NULL, expected)))
+        goto err;
+
+    if (encap) {
+        if (!TEST_int_eq(EVP_PKEY_auth_encapsulate_init(ctx, auth,
+                                                        opparam), expected))
+            goto err;
+    } else {
+        if (!TEST_int_eq(EVP_PKEY_auth_decapsulate_init(ctx, auth,
+                                                        opparam), expected))
+            goto err;
+    }
+    if (expected == 0
+        && !TEST_int_eq(ERR_GET_REASON(ERR_get_error()),
+                        PROV_R_NOT_A_PUBLIC_KEY))
+        goto err;
+    ret = 1;
+err:
+    EVP_PKEY_free(auth);
+    return ret;
+}
+
+/* EC specific tests */
+
+/* Perform EC DHKEM KATs */
+static int test_ec_dhkem_derivekey(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY *pkey = NULL;
+    OSSL_PARAM params[3];
+    EVP_PKEY_CTX *genctx = NULL;
+    const TEST_DERIVEKEY_DATA *t = &ec_derivekey_data[tstid];
+    unsigned char pubkey[133];
+    unsigned char privkey[66];
+    size_t pubkeylen = 0, privkeylen = 0;
+    BIGNUM *priv = NULL;
+
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                 (char *)t->curvename, 0);
+    params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                  (char *)t->ikm, t->ikmlen);
+    params[2] = OSSL_PARAM_construct_end();
+
+    ret = TEST_ptr(genctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL))
+          && TEST_int_eq(EVP_PKEY_keygen_init(genctx), 1)
+          && TEST_int_eq(EVP_PKEY_CTX_set_params(genctx, params), 1)
+          && TEST_int_eq(EVP_PKEY_generate(genctx, &pkey), 1)
+          && TEST_true(EVP_PKEY_get_octet_string_param(pkey,
+                           OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                           pubkey, sizeof(pubkey), &pubkeylen))
+          && TEST_true(EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY,
+                                             &priv))
+          && TEST_int_gt(privkeylen = BN_bn2bin(priv, privkey), 0)
+          && TEST_int_le(privkeylen, sizeof(privkey))
+          && TEST_mem_eq(privkey, privkeylen, t->priv, t->privlen)
+          && TEST_mem_eq(pubkey, pubkeylen, t->pub, t->publen);
+
+    BN_free(priv);
+    EVP_PKEY_free(pkey);
+    EVP_PKEY_CTX_free(genctx);
+    return ret;
+}
+
+/*
+ * Test that encapsulation uses a random seed if the ikm is not specified,
+ * and verify that the shared secret matches the decapsulate result.
+ */
+static int test_ec_noikme(int tstid)
+{
+    int ret = 0, auth = 0;
+    EVP_PKEY_CTX *ctx = NULL;
+    EVP_PKEY *recip = NULL;
+    EVP_PKEY *sender_auth = NULL;
+    unsigned char sender_secret[256];
+    unsigned char recip_secret[256];
+    unsigned char sender_pub[256];
+    size_t sender_secretlen = sizeof(sender_secret);
+    size_t recip_secretlen = sizeof(recip_secret);
+    size_t sender_publen = sizeof(sender_pub);
+    const char *curve;
+    int sz = OSSL_NELEM(dhkem_supported_curves);
+    const char *op = OSSL_KEM_PARAM_OPERATION_DHKEM;
+
+    if (tstid >= sz) {
+        auth = 1;
+        tstid -= sz;
+    }
+    curve = dhkem_supported_curves[tstid];
+    TEST_note("testing encap/decap of curve %s%s\n", curve,
+              auth ? " with auth" : "");
+
+    if (curve[0] == 'X') {
+        if (!TEST_ptr(recip = EVP_PKEY_Q_keygen(libctx, NULL, curve))
+                || (auth
+                    && !TEST_ptr(sender_auth = EVP_PKEY_Q_keygen(libctx, NULL,
+                                                                 curve))))
+            goto err;
+    } else {
+        if (!TEST_ptr(recip = EVP_PKEY_Q_keygen(libctx, NULL, "EC", curve))
+                || (auth
+                    && !TEST_ptr(sender_auth = EVP_PKEY_Q_keygen(libctx, NULL,
+                                                                 "EC", curve))))
+            goto err;
+    }
+
+    ret = TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, recip, NULL))
+          && (sender_auth == NULL
+              || TEST_int_eq(EVP_PKEY_auth_encapsulate_init(ctx, sender_auth,
+                                                            NULL), 1))
+          && (sender_auth != NULL
+              || TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), 1))
+          && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(ctx, op), 1)
+          && TEST_int_eq(EVP_PKEY_encapsulate(ctx, sender_pub, &sender_publen,
+                                              sender_secret, &sender_secretlen), 1)
+          && (sender_auth == NULL
+              || TEST_int_eq(EVP_PKEY_auth_decapsulate_init(ctx, sender_auth,
+                                                            NULL), 1))
+          && (sender_auth != NULL
+              || TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, NULL), 1))
+          && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(ctx, op), 1)
+          && TEST_int_eq(EVP_PKEY_decapsulate(ctx, recip_secret, &recip_secretlen,
+                                             sender_pub, sender_publen), 1)
+          && TEST_mem_eq(recip_secret, recip_secretlen,
+                         sender_secret, sender_secretlen);
+err:
+    EVP_PKEY_CTX_free(ctx);
+    EVP_PKEY_free(sender_auth);
+    EVP_PKEY_free(recip);
+    return ret;
+}
+
+/* Test encap/decap init fail if the curve is invalid */
+static int do_ec_curve_failtest(const char *curve)
+{
+    int ret;
+    EVP_PKEY *key = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+
+    ret = TEST_ptr(key = EVP_PKEY_Q_keygen(libctx, NULL, "EC", curve))
+          && TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL))
+          && TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), -2)
+          && TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, NULL), -2);
+    EVP_PKEY_free(key);
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
+static int test_ec_curve_nonnist(void)
+{
+    return do_ec_curve_failtest("secp256k1");
+}
+
+static int test_ec_curve_unsupported(void)
+{
+    return do_ec_curve_failtest("P-224");
+}
+
+/* Test that passing a bad recipient public EC key fails during encap/decap */
+static int test_ec_badpublic(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY *recippriv = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+    unsigned char secret[256];
+    unsigned char pub[256];
+    size_t secretlen = sizeof(secret);
+    int encap = ((tstid & 1) == 0);
+    const TEST_ENCAPDATA *t = &ec_encapdata[0];
+
+    TEST_note("%s %s", t->curve, encap ? "Encap" : "Decap");
+    /* Set the recipient public key to the point at infinity */
+    pub[0] = 0;
+    if (!TEST_ptr(recippriv = new_raw_private_key(t->curve, t->rpriv, t->rprivlen,
+                                                  pub, 1)))
+        goto err;
+
+    if (!TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, recippriv, NULL)))
+        goto err;
+
+    if (encap) {
+        unsigned char enc[256];
+        size_t enclen = sizeof(enc);
+
+        if (!TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, opparam), 1))
+            goto err;
+        if (!TEST_int_eq(EVP_PKEY_encapsulate(ctx, enc , &enclen,
+                                              secret, &secretlen), 0 ))
+            goto err;
+    } else {
+        if (!TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, opparam), 1))
+            goto err;
+        if (!TEST_int_eq(EVP_PKEY_decapsulate(ctx, secret, &secretlen,
+                                              t->expected_enc,
+                                              t->expected_enclen),
+                         0))
+            goto err;
+    }
+    if (!TEST_int_eq(ERR_GET_REASON(ERR_get_error()), PROV_R_INVALID_KEY))
+        goto err;
+    ret = 1;
+err:
+    EVP_PKEY_free(recippriv);
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
+static int test_ec_badauth(int tstid)
+{
+    int ret = 0;
+    EVP_PKEY *auth = NULL;
+    unsigned char enc[256];
+    unsigned char secret[256];
+    unsigned char pub[256];
+    size_t enclen = sizeof(enc);
+    size_t secretlen = sizeof(secret);
+    int encap = ((tstid & 1) == 0);
+    const TEST_ENCAPDATA *t = &ec_encapdata[TEST_KEYTYPE_P256];
+    EVP_PKEY_CTX *ctx = rctx[TEST_KEYTYPE_P256];
+
+    TEST_note("%s %s", t->curve, encap ? "Encap" : "Decap");
+    /* Set the auth public key to the point at infinity */
+    pub[0] = 0;
+    if (!TEST_ptr(auth = new_raw_private_key(t->curve, t->rpriv, t->rprivlen,
+                                             pub, 1)))
+        goto err;
+    if (encap) {
+        if (!TEST_int_eq(EVP_PKEY_auth_encapsulate_init(ctx, auth,
+                                                        opparam), 1)
+            || !TEST_int_eq(EVP_PKEY_encapsulate(ctx, enc, &enclen,
+                                                 secret, &secretlen), 0))
+            goto err;
+    } else {
+        if (!TEST_int_eq(EVP_PKEY_auth_decapsulate_init(ctx, auth, opparam), 1)
+            || !TEST_int_eq(EVP_PKEY_decapsulate(ctx, secret, &secretlen,
+                                                 t->expected_enc,
+                                                 t->expected_enclen), 0))
+            goto err;
+    }
+    if (!TEST_int_eq(ERR_GET_REASON(ERR_get_error()), PROV_R_INVALID_KEY))
+        goto err;
+    ret = 1;
+err:
+    EVP_PKEY_free(auth);
+    return ret;
+}
+
+static int test_ec_invalid_decap_enc_buffer(void)
+{
+    const TEST_ENCAPDATA *t = &ec_encapdata[TEST_KEYTYPE_P256];
+    unsigned char enc[256];
+    unsigned char secret[256];
+    size_t secretlen = sizeof(secret);
+    EVP_PKEY_CTX *ctx = rctx[0];
+
+    memcpy(enc, t->expected_enc, t->expected_enclen);
+    enc[0] = 0xFF;
+
+    return TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, opparam), 1)
+           && TEST_int_eq(EVP_PKEY_decapsulate(ctx, secret, &secretlen,
+                                               enc, t->expected_enclen), 0);
+}
+
+/* ECX specific tests */
+
+/* Perform ECX DHKEM KATs */
+static int test_ecx_dhkem_derivekey(int tstid)
+{
+    int ret;
+    OSSL_PARAM params[2];
+    EVP_PKEY_CTX *genctx;
+    EVP_PKEY *pkey = NULL;
+    unsigned char pubkey[64];
+    unsigned char privkey[64];
+    unsigned char masked_priv[64];
+    size_t pubkeylen = 0, privkeylen = 0;
+    const TEST_DERIVEKEY_DATA *t = &ecx_derivekey_data[tstid];
+
+    memcpy(masked_priv, t->priv, t->privlen);
+    if (OPENSSL_strcasecmp(t->curvename, "X25519") == 0) {
+        /*
+         * The RFC test vector seems incorrect since it is not in serialized form,
+         * So manually do the conversion here for now.
+         */
+        masked_priv[0] &= 248;
+        masked_priv[t->privlen - 1] &= 127;
+        masked_priv[t->privlen - 1] |= 64;
+    } else {
+        masked_priv[0] &= 252;
+        masked_priv[t->privlen - 1] |= 128;
+    }
+
+    params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                  (char *)t->ikm, t->ikmlen);
+    params[1] = OSSL_PARAM_construct_end();
+
+    ret = TEST_ptr(genctx = EVP_PKEY_CTX_new_from_name(libctx, t->curvename, NULL))
+          && TEST_int_eq(EVP_PKEY_keygen_init(genctx), 1)
+          && TEST_int_eq(EVP_PKEY_CTX_set_params(genctx, params), 1)
+          && TEST_int_eq(EVP_PKEY_keygen(genctx, &pkey), 1)
+          && TEST_int_eq(EVP_PKEY_get_octet_string_param(pkey,
+                             OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                             pubkey, sizeof(pubkey), &pubkeylen), 1)
+          && TEST_int_eq(EVP_PKEY_get_octet_string_param(pkey,
+                             OSSL_PKEY_PARAM_PRIV_KEY,
+                             privkey, sizeof(privkey), &privkeylen), 1)
+          && TEST_mem_eq(t->pub, t->publen, pubkey, pubkeylen)
+          && TEST_mem_eq(masked_priv, t->privlen, privkey, privkeylen);
+
+    EVP_PKEY_free(pkey);
+    EVP_PKEY_CTX_free(genctx);
+    return ret;
+}
+
+/* Fail if the auth key has a different curve */
+static int test_ecx_auth_key_curve_mismatch(void)
+{
+    int ret = 0;
+    EVP_PKEY *auth = NULL;
+
+    if (!TEST_ptr(auth = EVP_PKEY_Q_keygen(libctx, NULL, "X448")))
+        return 0;
+
+    ret = TEST_int_eq(EVP_PKEY_auth_encapsulate_init(rctx[TEST_KEYTYPE_X25519],
+                                                     auth, opparam), 0);
+    EVP_PKEY_free(auth);
+    return ret;
+}
+
+/* Fail if ED448 is used for DHKEM */
+static int test_ed_curve_unsupported(void)
+{
+    int ret;
+    EVP_PKEY *key = NULL;
+    EVP_PKEY_CTX *ctx = NULL;
+
+    ret = TEST_ptr(key = EVP_PKEY_Q_keygen(libctx, NULL, "ED448"))
+          && TEST_ptr(ctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL))
+          && TEST_int_eq(EVP_PKEY_encapsulate_init(ctx, NULL), -2)
+          && TEST_int_eq(EVP_PKEY_decapsulate_init(ctx, NULL), -2);
+    EVP_PKEY_free(key);
+    EVP_PKEY_CTX_free(ctx);
+    return ret;
+}
+
+int setup_tests(void)
+{
+    const char *prov_name = "default";
+    char *config_file = NULL;
+    char *op = OSSL_KEM_PARAM_OPERATION_DHKEM;
+
+    if (!test_get_libctx(&libctx, &nullprov, config_file, &libprov, prov_name))
+        return 0;
+    opparam[0] = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION,
+                                                  op, 0);
+    opparam[1] = OSSL_PARAM_construct_end();
+
+    /* Create P256 and X25519 keys and ctxs */
+    if (!TEST_ptr(rkey[TEST_KEYTYPE_P256] = EVP_PKEY_Q_keygen(libctx, NULL,
+                                                              "EC", "P-256")))
+        goto err;
+    if (!TEST_ptr(rkey[TEST_KEYTYPE_X25519] = EVP_PKEY_Q_keygen(libctx, NULL,
+                                                                "X25519")))
+        goto err;
+    if (!TEST_ptr(rctx[TEST_KEYTYPE_P256] =
+                      EVP_PKEY_CTX_new_from_pkey(libctx,
+                                                 rkey[TEST_KEYTYPE_P256], NULL)))
+       goto err;
+    if (!TEST_ptr(rctx[TEST_KEYTYPE_X25519] =
+                      EVP_PKEY_CTX_new_from_pkey(libctx,
+                                                 rkey[TEST_KEYTYPE_X25519], NULL)))
+       goto err;
+
+    ADD_ALL_TESTS(test_dhkem_encapsulate, OSSL_NELEM(ec_encapdata));
+    ADD_ALL_TESTS(test_dhkem_decapsulate, OSSL_NELEM(ec_encapdata));
+    ADD_ALL_TESTS(test_settables, TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_init_multiple, TEST_KEYTYPES_P256_X25519);
+
+    ADD_ALL_TESTS(test_auth_key_type_mismatch, TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_no_operation_set, TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_ikm_small, TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_input_size_small, TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_null_params, TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_set_params, TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_nopublic,
+                  TEST_KEM_ENCAP_DECAP * TEST_KEYTYPES_P256_X25519);
+    ADD_ALL_TESTS(test_noauthpublic,
+                  TEST_KEM_ENCAP_DECAP * TEST_KEYTYPES_P256_X25519);
+
+    /* EC Specific tests */
+    ADD_ALL_TESTS(test_ec_dhkem_derivekey, OSSL_NELEM(ec_derivekey_data));
+    ADD_ALL_TESTS(test_ec_noikme,
+                  TEST_TYPE_AUTH_NOAUTH * OSSL_NELEM(dhkem_supported_curves));
+    ADD_TEST(test_ec_auth_key_curve_mismatch);
+    ADD_TEST(test_ec_invalid_private_key);
+    ADD_TEST(test_ec_dhkem_derivekey_fail);
+    ADD_TEST(test_ec_curve_nonnist);
+    ADD_TEST(test_ec_curve_unsupported);
+    ADD_TEST(test_ec_invalid_decap_enc_buffer);
+    ADD_TEST(test_ec_public_key_infinity);
+    ADD_ALL_TESTS(test_ec_badpublic, TEST_KEM_ENCAP_DECAP);
+    ADD_ALL_TESTS(test_ec_badauth, TEST_KEM_ENCAP_DECAP);
+
+    /* ECX specific tests */
+    ADD_ALL_TESTS(test_ecx_dhkem_derivekey, OSSL_NELEM(ecx_derivekey_data));
+    ADD_TEST(test_ecx_auth_key_curve_mismatch);
+    ADD_TEST(test_ed_curve_unsupported);
+    return 1;
+err:
+    return 0;
+}
+
+void cleanup_tests(void)
+{
+    EVP_PKEY_free(rkey[1]);
+    EVP_PKEY_free(rkey[0]);
+    EVP_PKEY_CTX_free(rctx[1]);
+    EVP_PKEY_CTX_free(rctx[0]);
+    OSSL_PROVIDER_unload(libprov);
+    OSSL_LIB_CTX_free(libctx);
+    OSSL_PROVIDER_unload(nullprov);
+}
diff --git a/test/recipes/30-test_evp_pkey_dhkem.t b/test/recipes/30-test_evp_pkey_dhkem.t
new file mode 100644 (file)
index 0000000..c1486fc
--- /dev/null
@@ -0,0 +1,18 @@
+#! /usr/bin/env perl
+# Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the Apache License 2.0 (the "License").  You may not use
+# this file except in compliance with the License.  You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use OpenSSL::Test;
+use OpenSSL::Test::Simple;
+use OpenSSL::Test::Utils;
+
+setup("test_evp_pkey_dhkem");
+
+plan skip_all => "This test is unsupported in a no-ec build" if disabled("ec");
+
+simple_test("test_evp_pkey_dhkem", "evp_pkey_dhkem_test");
index 50be3df6de8adf94784e2d95622f9e2a8e545400..5e6c85b6385933a28bfd861806c905f0917e237e 100644 (file)
@@ -5461,3 +5461,5 @@ X509_get_default_cert_uri_env           ? 3_1_0   EXIST::FUNCTION:
 X509_get_default_cert_path_env          ?      3_1_0   EXIST::FUNCTION:
 BIO_s_dgram_pair                        ?      3_1_0   EXIST::FUNCTION:DGRAM
 BIO_new_bio_dgram_pair                  ?      3_1_0   EXIST::FUNCTION:DGRAM
+EVP_PKEY_auth_encapsulate_init          ?      3_1_0   EXIST::FUNCTION:
+EVP_PKEY_auth_decapsulate_init          ?      3_1_0   EXIST::FUNCTION: