[openssl] master update

nic.tuv at gmail.com nic.tuv at gmail.com
Tue Feb 18 17:13:36 UTC 2020


The branch master has been updated
       via  ce82b892e8b86d68d02096554b4e07af7f095368 (commit)
       via  2d9167ed0b588dacbdd0303fb6041ffe1d8b3a92 (commit)
       via  0401d766afcd022748763f5614188301c9856c6e (commit)
       via  a377871db10afcdfb080c79f3245baf441fe07fc (commit)
       via  4fe54d674f14e7964f982285d1aeb86698a33c3c (commit)
       via  cf6404b14198b96a882affe917bb337e2626136c (commit)
       via  cd701de96a147260c2290d85af8a0656120a8ff8 (commit)
       via  afa2b389bc0b81a976bf79381ecc553932a090b3 (commit)
      from  0d51cf3ccc0224def10c32b6defd4a77a1b4322a (commit)


- Log -----------------------------------------------------------------
commit ce82b892e8b86d68d02096554b4e07af7f095368
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Sun Feb 9 13:56:27 2020 +0200

    [PROV][EC] Update documentation
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

commit 2d9167ed0b588dacbdd0303fb6041ffe1d8b3a92
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Tue Jan 21 17:08:16 2020 +0200

    [BN] harden `BN_copy()` against leaks from memory accesses
    
    `BN_copy()` (and indirectly `BN_dup()`) do not propagate the
    `BN_FLG_CONSTTIME` flag: the propagation has been turned on and off a
    few times in the past years, because in some conditions it has shown
    unintended consequences in some code paths.
    
    Without turning the propagation on once more, we can still improve
    `BN_copy()` by avoiding to leak `src->top` in case `src` is flagged with
    `BN_FLG_CONSTTIME`.
    In this case we can instead use `src->dmax` as the number of words
    allocated for `dst` and for the `memcpy` operation.
    
    Barring compiler or runtime optimizations, if the caller provides `src`
    flagged as const time and preallocated to a public size, no leak should
    happen due to the copy operation.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

commit 0401d766afcd022748763f5614188301c9856c6e
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Tue Jan 21 17:00:41 2020 +0200

    [EC] harden EC_KEY against leaks from memory accesses
    
    We should never leak the bit length of the secret scalar in the key,
    so we always set the `BN_FLG_CONSTTIME` flag on the internal `BIGNUM`
    holding the secret scalar.
    
    This is important also because `BN_dup()` (and `BN_copy()`) do not
    propagate the `BN_FLG_CONSTTIME` flag from the source `BIGNUM`, and
    this brings an extra risk of inadvertently losing the flag, even when
    the called specifically set it.
    
    The propagation has been turned on and off a few times in the past
    years because in some conditions has shown unintended consequences in
    some code paths, so at the moment we can't fix this in the BN layer.
    
    In `EC_KEY_set_private_key()` we can work around the propagation by
    manually setting the flag after `BN_dup()` as we know for sure that
    inside the EC module the `BN_FLG_CONSTTIME` is always treated
    correctly and should not generate unintended consequences.
    
    Setting the `BN_FLG_CONSTTIME` flag alone is never enough, we also have
    to preallocate the `BIGNUM` internal buffer to a fixed public size big
    enough that operations performed during the processing never trigger
    a realloc which would leak the size of the scalar through memory
    accesses.
    
    Fixed Length
    ------------
    
    The order of the large prime subgroup of the curve is our choice for
    a fixed public size, as that is generally the upper bound for
    generating a private key in EC cryptosystems and should fit all valid
    secret scalars.
    
    For preallocating the `BIGNUM` storage we look at the number of "words"
    required for the internal representation of the order, and we
    preallocate 2 extra "words" in case any of the subsequent processing
    might temporarily overflow the order length.
    
    Future work
    -----------
    
    A separate commit addresses further hardening of `BN_copy()` (and
    indirectly `BN_dup()`).
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

commit a377871db10afcdfb080c79f3245baf441fe07fc
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Tue Jan 21 16:48:49 2020 +0200

    [PROV][KEYMGMT][EC] Import/export of priv_key as padded const time BN
    
    For EC keys it is particularly important to avoid leaking the bit length
    of the secret scalar.
    
    Key import/export should never leak the bit length of the secret
    scalar in the key.
    
    For this reason, on export we use padded BIGNUMs with fixed length,
    using the new `ossl_param_bld_push_BN_pad()`.
    
    When importing we also should make sure that, even if short lived,
    the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as
    soon as possible, so that any processing of this BIGNUM might opt for
    constant time implementations in the backend.
    
    Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
    to preallocate the BIGNUM internal buffer to a fixed size big enough
    that operations performed during the processing never trigger a
    realloc which would leak the size of the scalar through memory
    accesses.
    
    Fixed length
    ------------
    
    The order of the large prime subgroup of the curve is our choice for
    a fixed public size, as that is generally the upper bound for
    generating a private key in EC cryptosystems and should fit all valid
    secret scalars.
    
    For padding on export we just use the bit length of the order
    converted to bytes (rounding up).
    
    For preallocating the BIGNUM storage we look at the number of "words"
    required for the internal representation of the order, and we
    preallocate 2 extra "words" in case any of the subsequent processing
    might temporarily overflow the order length.
    
    Future work
    -----------
    
    To ensure the flag and fixed size preallocation persists upon
    `EC_KEY_set_private_key()`, we need to further harden
    `EC_KEY_set_private_key()` and `BN_copy()`.
    This is done in separate commits.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

commit 4fe54d674f14e7964f982285d1aeb86698a33c3c
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Sun Dec 15 00:20:53 2019 +0200

    [PROV][KMGMT][KEXCH][EC] Implement EC keymgtm and ECDH
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

commit cf6404b14198b96a882affe917bb337e2626136c
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Sat Jan 25 18:19:56 2020 +0200

    [CMS] Test decryption of a ciphertext encrypted from 1.1.1
    
    Current CMS en/decryption tests only validate that our current decyption
    and encryption algorithms are compatible, but they say nothing about
    correctness of the output for the given set of parameters.
    
    As a partial fix in absence of proper KAT tests, we decrypt ciphertexts
    generated with OpenSSL 1.1.1.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

commit cd701de96a147260c2290d85af8a0656120a8ff8
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Tue Jan 7 01:19:13 2020 +0200

    [EC] Constify internal EC_KEY pointer usage
    
    A pair of internal functions related to EC_KEY handling could benefit
    from declaring `EC_KEY *` variables as `const`, providing clarity for
    callers and readers of the code, in addition to enlisting the compiler
    in preventing some mistakes.
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

commit afa2b389bc0b81a976bf79381ecc553932a090b3
Author: Nicola Tuveri <nic.tuv at gmail.com>
Date:   Sun Dec 15 00:29:34 2019 +0200

    [PROV][KEYMGMT][DH][DSA] use BN_clear_free for secrets
    
    Reviewed-by: Matt Caswell <matt at openssl.org>
    Reviewed-by: Richard Levitte <levitte at openssl.org>
    Reviewed-by: Shane Lontis <shane.lontis at oracle.com>
    (Merged from https://github.com/openssl/openssl/pull/10631)

-----------------------------------------------------------------------

Summary of changes:
 crypto/bn/bn_lib.c                                 |   8 +-
 crypto/ec/build.info                               |   4 +-
 crypto/ec/ec_ameth.c                               | 177 ++++-
 crypto/ec/ec_asn1.c                                |   5 +
 crypto/ec/ec_evp_lib.c                             | 422 ++++++++++++
 crypto/ec/ec_key.c                                 | 123 +++-
 crypto/ec/ec_local.h                               |   3 +
 crypto/evp/evp_local.h                             |   4 +
 crypto/evp/exchange.c                              |  26 +-
 crypto/evp/keymgmt_meth.c                          |  37 +-
 crypto/evp/pmeth_lib.c                             | 165 ++++-
 doc/man7/provider-keyexch.pod                      |  98 ++-
 doc/man7/provider-keymgmt.pod                      |  67 +-
 include/crypto/evp.h                               |  19 +
 include/openssl/core_names.h                       |  30 +-
 include/openssl/core_numbers.h                     |  12 +
 include/openssl/ec.h                               |  56 +-
 providers/defltprov.c                              |   2 +
 providers/implementations/exchange/build.info      |   2 +
 providers/implementations/exchange/ecdh_exch.c     | 533 +++++++++++++++
 .../implementations/include/prov/implementations.h |   2 +
 providers/implementations/keymgmt/build.info       |   4 +
 providers/implementations/keymgmt/dh_kmgmt.c       |   2 +-
 providers/implementations/keymgmt/dsa_kmgmt.c      |   2 +-
 providers/implementations/keymgmt/ec_kmgmt.c       | 721 +++++++++++++++++++++
 .../implementations/keymgmt/ec_kmgmt_imexport.inc  | 100 +++
 test/recipes/80-test_cms.t                         |  20 +-
 .../80-test_cms_data/ciphertext_from_1_1_1.cms     |  20 +
 util/libcrypto.num                                 |  10 +
 29 files changed, 2548 insertions(+), 126 deletions(-)
 create mode 100644 crypto/ec/ec_evp_lib.c
 create mode 100644 providers/implementations/exchange/ecdh_exch.c
 create mode 100644 providers/implementations/keymgmt/ec_kmgmt.c
 create mode 100644 providers/implementations/keymgmt/ec_kmgmt_imexport.inc
 create mode 100644 test/recipes/80-test_cms_data/ciphertext_from_1_1_1.cms

diff --git a/crypto/bn/bn_lib.c b/crypto/bn/bn_lib.c
index 1e62b96874..e7ffce2875 100644
--- a/crypto/bn/bn_lib.c
+++ b/crypto/bn/bn_lib.c
@@ -322,15 +322,19 @@ BIGNUM *BN_dup(const BIGNUM *a)
 
 BIGNUM *BN_copy(BIGNUM *a, const BIGNUM *b)
 {
+    int bn_words;
+
     bn_check_top(b);
 
+    bn_words = BN_get_flags(b, BN_FLG_CONSTTIME) ? b->dmax : b->top;
+
     if (a == b)
         return a;
-    if (bn_wexpand(a, b->top) == NULL)
+    if (bn_wexpand(a, bn_words) == NULL)
         return NULL;
 
     if (b->top > 0)
-        memcpy(a->d, b->d, sizeof(b->d[0]) * b->top);
+        memcpy(a->d, b->d, sizeof(b->d[0]) * bn_words);
 
     a->neg = b->neg;
     a->top = b->top;
diff --git a/crypto/ec/build.info b/crypto/ec/build.info
index a8828c5102..0e01d4af38 100644
--- a/crypto/ec/build.info
+++ b/crypto/ec/build.info
@@ -43,8 +43,6 @@ IF[{- !$disabled{asm} -}]
   ENDIF
 ENDIF
 
-LIBS=../../libcrypto
-
 $COMMON=ec_lib.c ecp_smpl.c ecp_mont.c ecp_nist.c ec_cvt.c ec_mult.c \
         ec_curve.c ec_check.c ec_print.c ec_key.c ec_asn1.c \
         ec2_smpl.c \
@@ -55,7 +53,7 @@ $COMMON=ec_lib.c ecp_smpl.c ecp_mont.c ecp_nist.c ec_cvt.c ec_mult.c \
         curve448/curve448_tables.c curve448/eddsa.c curve448/curve448.c \
         $ECASM
 SOURCE[../../libcrypto]=$COMMON ec_ameth.c ec_pmeth.c ecx_meth.c ecx_key.c \
-                        ec_err.c ecdh_kdf.c eck_prn.c
+                        ec_err.c ecdh_kdf.c eck_prn.c ec_evp_lib.c
 SOURCE[../../providers/libfips.a]=$COMMON
 
 # Implementations are now spread across several libraries, so the defines
diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c
index d2c8c399de..d6807661ff 100644
--- a/crypto/ec/ec_ameth.c
+++ b/crypto/ec/ec_ameth.c
@@ -22,6 +22,8 @@
 #include <openssl/asn1t.h>
 #include "crypto/asn1.h"
 #include "crypto/evp.h"
+#include <openssl/core_names.h>
+#include "internal/param_build.h"
 #include "ec_local.h"
 
 #ifndef OPENSSL_NO_CMS
@@ -29,7 +31,7 @@ static int ecdh_cms_decrypt(CMS_RecipientInfo *ri);
 static int ecdh_cms_encrypt(CMS_RecipientInfo *ri);
 #endif
 
-static int eckey_param2type(int *pptype, void **ppval, EC_KEY *ec_key)
+static int eckey_param2type(int *pptype, void **ppval, const EC_KEY *ec_key)
 {
     const EC_GROUP *group;
     int nid;
@@ -63,7 +65,7 @@ static int eckey_param2type(int *pptype, void **ppval, EC_KEY *ec_key)
 
 static int eckey_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey)
 {
-    EC_KEY *ec_key = pkey->pkey.ec;
+    const EC_KEY *ec_key = pkey->pkey.ec;
     void *pval = NULL;
     int ptype;
     unsigned char *penc = NULL, *p;
@@ -574,6 +576,167 @@ static int ec_pkey_param_check(const EVP_PKEY *pkey)
     return EC_GROUP_check(eckey->group, NULL);
 }
 
+static
+size_t ec_pkey_dirty_cnt(const EVP_PKEY *pkey)
+{
+    return pkey->pkey.ec->dirty_cnt;
+}
+
+static ossl_inline
+int ecparams_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl)
+{
+    const EC_GROUP *ecg;
+    int curve_nid;
+
+    if (eckey == NULL)
+        return 0;
+
+    ecg = EC_KEY_get0_group(eckey);
+    if (ecg == NULL)
+        return 0;
+
+    curve_nid = EC_GROUP_get_curve_name(ecg);
+
+    if (curve_nid == NID_undef) {
+        /* explicit parameters */
+
+        /*
+         * TODO(3.0): should we support explicit parameters curves?
+         */
+        return 0;
+    } else {
+        /* named curve */
+        const char *curve_name = NULL;
+
+        if ((curve_name = OBJ_nid2sn(curve_nid)) == NULL)
+            return 0;
+
+        if (!ossl_param_bld_push_utf8_string(tmpl, OSSL_PKEY_PARAM_EC_NAME, curve_name, 0))
+            return 0;
+    }
+
+    return 1;
+}
+
+static
+int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata,
+                      EVP_KEYMGMT *to_keymgmt)
+{
+    const EC_KEY *eckey = NULL;
+    const EC_GROUP *ecg = NULL;
+    unsigned char *pub_key_buf = NULL;
+    size_t pub_key_buflen;
+    OSSL_PARAM_BLD tmpl;
+    OSSL_PARAM *params = NULL;
+    const BIGNUM *priv_key = NULL;
+    const EC_POINT *pub_point = NULL;
+    int rv = 0;
+
+    if (from == NULL
+            || (eckey = from->pkey.ec) == NULL
+            || (ecg = EC_KEY_get0_group(eckey)) == NULL)
+        return 0;
+
+    ossl_param_bld_init(&tmpl);
+
+    /* export the domain parameters */
+    if (!ecparams_to_params(eckey, &tmpl))
+        return 0;
+
+    priv_key = EC_KEY_get0_private_key(eckey);
+    pub_point = EC_KEY_get0_public_key(eckey);
+
+    /* public_key must be present, priv_key is optional */
+    if (pub_point == NULL)
+        return 0;
+
+    /* convert pub_point to a octet string according to the SECG standard */
+    if ((pub_key_buflen = EC_POINT_point2buf(ecg, pub_point,
+                                             POINT_CONVERSION_COMPRESSED,
+                                             &pub_key_buf, NULL)) == 0)
+        return 0;
+
+    if (!ossl_param_bld_push_octet_string(&tmpl,
+                OSSL_PKEY_PARAM_PUB_KEY,
+                pub_key_buf,
+                pub_key_buflen))
+        goto err;
+
+    if (priv_key != NULL) {
+        size_t sz;
+        int ecbits;
+        int ecdh_cofactor_mode;
+
+        /*
+         * Key import/export should never leak the bit length of the secret
+         * scalar in the key.
+         *
+         * For this reason, on export we use padded BIGNUMs with fixed length.
+         *
+         * When importing we also should make sure that, even if short lived,
+         * the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as
+         * soon as possible, so that any processing of this BIGNUM might opt for
+         * constant time implementations in the backend.
+         *
+         * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
+         * to preallocate the BIGNUM internal buffer to a fixed public size big
+         * enough that operations performed during the processing never trigger
+         * a realloc which would leak the size of the scalar through memory
+         * accesses.
+         *
+         * Fixed Length
+         * ------------
+         *
+         * The order of the large prime subgroup of the curve is our choice for
+         * a fixed public size, as that is generally the upper bound for
+         * generating a private key in EC cryptosystems and should fit all valid
+         * secret scalars.
+         *
+         * For padding on export we just use the bit length of the order
+         * converted to bytes (rounding up).
+         *
+         * For preallocating the BIGNUM storage we look at the number of "words"
+         * required for the internal representation of the order, and we
+         * preallocate 2 extra "words" in case any of the subsequent processing
+         * might temporarily overflow the order length.
+         */
+        ecbits = EC_GROUP_order_bits(ecg);
+        if (ecbits <= 0)
+            goto err;
+
+        sz = (ecbits + 7 ) / 8;
+        if (!ossl_param_bld_push_BN_pad(&tmpl,
+                                        OSSL_PKEY_PARAM_PRIV_KEY,
+                                        priv_key, sz))
+            goto err;
+
+        /*
+         * The ECDH Cofactor Mode is defined only if the EC_KEY actually
+         * contains a private key, so we check for the flag and export it only
+         * in this case.
+         */
+        ecdh_cofactor_mode =
+            (EC_KEY_get_flags(eckey) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
+
+        /* Export the ECDH_COFACTOR_MODE parameter */
+        if (!ossl_param_bld_push_int(&tmpl,
+                                     OSSL_PKEY_PARAM_USE_COFACTOR_ECDH,
+                                     ecdh_cofactor_mode))
+            goto err;
+    }
+
+    params = ossl_param_bld_to_param(&tmpl);
+
+    /* We export, the provider imports */
+    rv = evp_keymgmt_import(to_keymgmt, to_keydata, OSSL_KEYMGMT_SELECT_ALL,
+                            params);
+
+ err:
+    ossl_param_bld_free(params);
+    OPENSSL_free(pub_key_buf);
+    return rv;
+}
+
 const EVP_PKEY_ASN1_METHOD eckey_asn1_meth = {
     EVP_PKEY_EC,
     EVP_PKEY_EC,
@@ -611,7 +774,15 @@ const EVP_PKEY_ASN1_METHOD eckey_asn1_meth = {
 
     ec_pkey_check,
     ec_pkey_public_check,
-    ec_pkey_param_check
+    ec_pkey_param_check,
+
+    0, /* set_priv_key */
+    0, /* set_pub_key */
+    0, /* get_priv_key */
+    0, /* get_pub_key */
+
+    ec_pkey_dirty_cnt,
+    ec_pkey_export_to
 };
 
 #if !defined(OPENSSL_NO_SM2)
diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c
index f61d8860a4..4f7a76a1a4 100644
--- a/crypto/ec/ec_asn1.c
+++ b/crypto/ec/ec_asn1.c
@@ -1051,6 +1051,7 @@ EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len)
         *a = ret;
     EC_PRIVATEKEY_free(priv_key);
     *in = p;
+    ret->dirty_cnt++;
     return ret;
 
  err:
@@ -1162,8 +1163,11 @@ EC_KEY *d2i_ECParameters(EC_KEY **a, const unsigned char **in, long len)
         ECerr(EC_F_D2I_ECPARAMETERS, ERR_R_EC_LIB);
         if (a == NULL || *a != ret)
              EC_KEY_free(ret);
+        else
+            ret->dirty_cnt++;
         return NULL;
     }
+    ret->dirty_cnt++;
 
     if (a)
         *a = ret;
@@ -1183,6 +1187,7 @@ EC_KEY *o2i_ECPublicKey(EC_KEY **a, const unsigned char **in, long len)
         return 0;
     }
     ret = *a;
+    /* EC_KEY_opt2key updates dirty_cnt */
     if (!EC_KEY_oct2key(ret, *in, len, NULL)) {
         ECerr(EC_F_O2I_ECPUBLICKEY, ERR_R_EC_LIB);
         return 0;
diff --git a/crypto/ec/ec_evp_lib.c b/crypto/ec/ec_evp_lib.c
new file mode 100644
index 0000000000..e4d7815993
--- /dev/null
+++ b/crypto/ec/ec_evp_lib.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2020 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>
+
+#include <openssl/err.h>
+#include <openssl/opensslv.h>
+
+#include <openssl/core_names.h>
+#include "crypto/evp.h"
+
+#include "ec_local.h"
+
+/*
+ * This file is meant to contain functions to provide EVP_PKEY support for EC
+ * keys.
+ */
+
+static ossl_inline
+int evp_pkey_ctx_getset_ecdh_param_checks(const EVP_PKEY_CTX *ctx)
+{
+    if (ctx == NULL || !EVP_PKEY_CTX_IS_DERIVE_OP(ctx)) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    /* If key type not EC return error */
+    if (ctx->pmeth != NULL && ctx->pmeth->pkey_id != EVP_PKEY_EC)
+        return -1;
+
+    return 1;
+}
+
+int EVP_PKEY_CTX_set_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx, int cofactor_mode)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /*
+     * Valid input values are:
+     *  * 0 for disable
+     *  * 1 for enable
+     *  * -1 for reset to default for associated priv key
+     */
+    if (cofactor_mode < -1 || cofactor_mode > 1) {
+        /* Uses the same return value of pkey_ec_ctrl() */
+        return -2;
+    }
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_EC_ECDH_COFACTOR,
+                                 cofactor_mode, NULL);
+
+    *p++ = OSSL_PARAM_construct_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE,
+                                    &cofactor_mode);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    return ret;
+}
+
+int EVP_PKEY_CTX_get_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx)
+{
+    int ret, mode;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_EC_ECDH_COFACTOR, -2, NULL);
+
+    *p++ = OSSL_PARAM_construct_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE,
+                                    &mode);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    if (mode < 0 || mode > 1) {
+        /*
+         * The provider should return either 0 or 1, any other value is a
+         * provider error.
+         */
+        return -1;
+    }
+
+    return mode;
+}
+
+int EVP_PKEY_CTX_set_ecdh_kdf_type(EVP_PKEY_CTX *ctx, int kdf)
+{
+    int ret;
+    const char *kdf_type;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    switch (kdf) {
+        case EVP_PKEY_ECDH_KDF_NONE:
+            kdf_type = "";
+            break;
+        case EVP_PKEY_ECDH_KDF_X9_63:
+            kdf_type = OSSL_KDF_NAME_X963KDF;
+            break;
+        default:
+            return -2;
+    }
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_EC_KDF_TYPE, kdf, NULL);
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
+                                            /*
+                                             * Cast away the const. This is read
+                                             * only so should be safe
+                                             */
+                                            (char *)kdf_type, 0);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+
+    return ret;
+}
+
+int EVP_PKEY_CTX_get_ecdh_kdf_type(EVP_PKEY_CTX *ctx)
+{
+    int ret;
+    /* 80 should be big enough */
+    char kdf_type[80];
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_EC_KDF_TYPE, -2, NULL);
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
+                                            kdf_type, sizeof(kdf_type));
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    if (kdf_type[0] == '\0')
+        return EVP_PKEY_ECDH_KDF_NONE;
+    else if (strcmp(kdf_type, OSSL_KDF_NAME_X963KDF) == 0)
+        return EVP_PKEY_ECDH_KDF_X9_63;
+
+    return -1;
+}
+
+int EVP_PKEY_CTX_set_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+    const char *md_name = NULL;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_EC_KDF_MD, 0, (void *)(md));
+
+    md_name = (md == NULL) ? "" : EVP_MD_name(md);
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
+                                            /*
+                                             * Cast away the const. This is read
+                                             * only so should be safe
+                                             */
+                                            (char *)md_name, 0);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+    return ret;
+}
+
+int EVP_PKEY_CTX_get_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD **pmd)
+{
+    /* 80 should be big enough */
+    char name[80] = "";
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_GET_EC_KDF_MD, 0, (void *)(pmd));
+
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
+                                            name, sizeof(name));
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    /* May be NULL meaning "unknown" */
+    *pmd = EVP_get_digestbyname(name);
+
+    return 1;
+}
+
+int EVP_PKEY_CTX_set_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int in)
+{
+    int ret;
+    size_t len = in;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_EC_KDF_OUTLEN, in, NULL);
+
+    if (in <= 0) {
+        /*
+         * This would ideally be -1 or 0, but we have to retain compatibility
+         * with legacy behaviour of EVP_PKEY_CTX_ctrl() which returned -2 if
+         * in <= 0
+         */
+        return -2;
+    }
+
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                       &len);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+    return ret;
+}
+
+int EVP_PKEY_CTX_get_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int *plen)
+{
+    size_t len = UINT_MAX;
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN, 0,
+                                 (void *)(plen));
+
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
+                                       &len);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    if (len > INT_MAX)
+        return -1;
+
+    *plen = (int)len;
+
+    return 1;
+}
+
+int EVP_PKEY_CTX_set0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm, int len)
+{
+    int ret;
+    OSSL_PARAM params[2], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_EC_KDF_UKM, len, (void *)(ukm));
+
+    *p++ = OSSL_PARAM_construct_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                            /*
+                                             * Cast away the const. This is read
+                                             * only so should be safe
+                                             */
+                                            (void *)ukm,
+                                            (size_t)len);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_set_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    }
+    if (ret == 1)
+        OPENSSL_free(ukm);
+    return ret;
+}
+
+int EVP_PKEY_CTX_get0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **pukm)
+{
+    size_t ukmlen;
+    int ret;
+    OSSL_PARAM params[3], *p = params;
+
+    ret = evp_pkey_ctx_getset_ecdh_param_checks(ctx);
+    if (ret != 1)
+        return ret;
+
+    /* TODO(3.0): Remove this eventually when no more legacy */
+    if (ctx->op.kex.exchprovctx == NULL)
+        return EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC,
+                                 EVP_PKEY_OP_DERIVE,
+                                 EVP_PKEY_CTRL_GET_EC_KDF_UKM, 0,
+                                 (void *)(pukm));
+
+    *p++ = OSSL_PARAM_construct_octet_ptr(OSSL_EXCHANGE_PARAM_KDF_UKM,
+                                          (void **)pukm, 0);
+    *p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_UKM_LEN,
+                                       &ukmlen);
+    *p++ = OSSL_PARAM_construct_end();
+
+    ret = evp_pkey_ctx_get_params_strict(ctx, params);
+    if (ret == -2) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED);
+        /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+        return -2;
+    } else if (ret != 1) {
+        return -1;
+    }
+
+    if (ukmlen > INT_MAX)
+        return -1;
+
+    return (int)ukmlen;
+}
diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c
index a0cd5b9bda..4c56777dfe 100644
--- a/crypto/ec/ec_key.c
+++ b/crypto/ec/ec_key.c
@@ -20,6 +20,7 @@
 #include "internal/refcount.h"
 #include <openssl/err.h>
 #include <openssl/engine.h>
+#include "crypto/bn.h"
 
 #ifndef FIPS_MODE
 EC_KEY *EC_KEY_new(void)
@@ -169,6 +170,8 @@ EC_KEY *EC_KEY_copy(EC_KEY *dest, const EC_KEY *src)
     if (src->meth->copy != NULL && src->meth->copy(dest, src) == 0)
         return NULL;
 
+    dest->dirty_cnt++;
+
     return dest;
 }
 
@@ -209,15 +212,28 @@ int EC_KEY_generate_key(EC_KEY *eckey)
         ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
-    if (eckey->meth->keygen != NULL)
-        return eckey->meth->keygen(eckey);
+    if (eckey->meth->keygen != NULL) {
+        int ret;
+
+        ret = eckey->meth->keygen(eckey);
+        if (ret == 1)
+            eckey->dirty_cnt++;
+
+        return ret;
+    }
     ECerr(EC_F_EC_KEY_GENERATE_KEY, EC_R_OPERATION_NOT_SUPPORTED);
     return 0;
 }
 
 int ossl_ec_key_gen(EC_KEY *eckey)
 {
-    return eckey->group->meth->keygen(eckey);
+    int ret;
+
+    ret = eckey->group->meth->keygen(eckey);
+
+    if (ret == 1)
+        eckey->dirty_cnt++;
+    return ret;
 }
 
 /*
@@ -287,6 +303,8 @@ int ec_key_simple_generate_key(EC_KEY *eckey)
     priv_key = NULL;
     pub_key = NULL;
 
+    eckey->dirty_cnt++;
+
     ok = 1;
 
 err:
@@ -305,12 +323,19 @@ err:
 
 int ec_key_simple_generate_public_key(EC_KEY *eckey)
 {
+    int ret;
+
     /*
      * See SP800-56AR3 5.6.1.2.2: Step (8)
      * pub_key = priv_key * G (where G is a point on the curve)
      */
-    return EC_POINT_mul(eckey->group, eckey->pub_key, eckey->priv_key, NULL,
-                        NULL, NULL);
+    ret = EC_POINT_mul(eckey->group, eckey->pub_key, eckey->priv_key, NULL,
+                       NULL, NULL);
+
+    if (ret == 1)
+        eckey->dirty_cnt++;
+
+    return ret;
 }
 
 int EC_KEY_check_key(const EC_KEY *eckey)
@@ -505,6 +530,7 @@ int EC_KEY_set_public_key_affine_coordinates(EC_KEY *key, BIGNUM *x,
         goto err;
     }
 
+    /* EC_KEY_set_public_key updates dirty_cnt */
     if (!EC_KEY_set_public_key(key, point))
         goto err;
 
@@ -532,6 +558,7 @@ int EC_KEY_set_group(EC_KEY *key, const EC_GROUP *group)
         return 0;
     EC_GROUP_free(key->group);
     key->group = EC_GROUP_dup(group);
+    key->dirty_cnt++;
     return (key->group == NULL) ? 0 : 1;
 }
 
@@ -542,17 +569,87 @@ const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key)
 
 int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key)
 {
+    int fixed_top;
+    const BIGNUM *order = NULL;
+    BIGNUM *tmp_key = NULL;
+
     if (key->group == NULL || key->group->meth == NULL)
         return 0;
+
+    /*
+     * Not only should key->group be set, but it should also be in a valid
+     * fully initialized state.
+     *
+     * Specifically, to operate in constant time, we need that the group order
+     * is set, as we use its length as the fixed public size of any scalar used
+     * as an EC private key.
+     */
+    order = EC_GROUP_get0_order(key->group);
+    if (order == NULL || BN_is_zero(order))
+        return 0; /* This should never happen */
+
     if (key->group->meth->set_private != NULL
         && key->group->meth->set_private(key, priv_key) == 0)
         return 0;
     if (key->meth->set_private != NULL
         && key->meth->set_private(key, priv_key) == 0)
         return 0;
+
+    /*
+     * We should never leak the bit length of the secret scalar in the key,
+     * so we always set the `BN_FLG_CONSTTIME` flag on the internal `BIGNUM`
+     * holding the secret scalar.
+     *
+     * This is important also because `BN_dup()` (and `BN_copy()`) do not
+     * propagate the `BN_FLG_CONSTTIME` flag from the source `BIGNUM`, and
+     * this brings an extra risk of inadvertently losing the flag, even when
+     * the called specifically set it.
+     *
+     * The propagation has been turned on and off a few times in the past
+     * years because in some conditions has shown unintended consequences in
+     * some code paths, so at the moment we can't fix this in the BN layer.
+     *
+     * In `EC_KEY_set_private_key()` we can work around the propagation by
+     * manually setting the flag after `BN_dup()` as we know for sure that
+     * inside the EC module the `BN_FLG_CONSTTIME` is always treated
+     * correctly and should not generate unintended consequences.
+     *
+     * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
+     * to preallocate the BIGNUM internal buffer to a fixed public size big
+     * enough that operations performed during the processing never trigger
+     * a realloc which would leak the size of the scalar through memory
+     * accesses.
+     *
+     * Fixed Length
+     * ------------
+     *
+     * The order of the large prime subgroup of the curve is our choice for
+     * a fixed public size, as that is generally the upper bound for
+     * generating a private key in EC cryptosystems and should fit all valid
+     * secret scalars.
+     *
+     * For preallocating the BIGNUM storage we look at the number of "words"
+     * required for the internal representation of the order, and we
+     * preallocate 2 extra "words" in case any of the subsequent processing
+     * might temporarily overflow the order length.
+     */
+    tmp_key = BN_dup(priv_key);
+    if (tmp_key == NULL)
+        return 0;
+
+    BN_set_flags(tmp_key, BN_FLG_CONSTTIME);
+
+    fixed_top = bn_get_top(order) + 2;
+    if (bn_wexpand(tmp_key, fixed_top) == NULL) {
+        BN_clear_free(tmp_key);
+        return 0;
+    }
+
     BN_clear_free(key->priv_key);
-    key->priv_key = BN_dup(priv_key);
-    return (key->priv_key == NULL) ? 0 : 1;
+    key->priv_key = tmp_key;
+    key->dirty_cnt++;
+
+    return 1;
 }
 
 const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key)
@@ -567,6 +664,7 @@ int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key)
         return 0;
     EC_POINT_free(key->pub_key);
     key->pub_key = EC_POINT_dup(pub_key, key->group);
+    key->dirty_cnt++;
     return (key->pub_key == NULL) ? 0 : 1;
 }
 
@@ -613,11 +711,13 @@ int EC_KEY_get_flags(const EC_KEY *key)
 void EC_KEY_set_flags(EC_KEY *key, int flags)
 {
     key->flags |= flags;
+    key->dirty_cnt++;
 }
 
 void EC_KEY_clear_flags(EC_KEY *key, int flags)
 {
     key->flags &= ~flags;
+    key->dirty_cnt++;
 }
 
 size_t EC_KEY_key2buf(const EC_KEY *key, point_conversion_form_t form,
@@ -639,6 +739,7 @@ int EC_KEY_oct2key(EC_KEY *key, const unsigned char *buf, size_t len,
         return 0;
     if (EC_POINT_oct2point(key->group, key->pub_key, buf, len, ctx) == 0)
         return 0;
+    key->dirty_cnt++;
     /*
      * Save the point conversion form.
      * For non-custom curves the first octet of the buffer (excluding
@@ -689,13 +790,18 @@ size_t ec_key_simple_priv2oct(const EC_KEY *eckey,
 
 int EC_KEY_oct2priv(EC_KEY *eckey, const unsigned char *buf, size_t len)
 {
+    int ret;
+
     if (eckey->group == NULL || eckey->group->meth == NULL)
         return 0;
     if (eckey->group->meth->oct2priv == NULL) {
         ECerr(EC_F_EC_KEY_OCT2PRIV, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
         return 0;
     }
-    return eckey->group->meth->oct2priv(eckey, buf, len);
+    ret = eckey->group->meth->oct2priv(eckey, buf, len);
+    if (ret == 1)
+        eckey->dirty_cnt++;
+    return ret;
 }
 
 int ec_key_simple_oct2priv(EC_KEY *eckey, const unsigned char *buf, size_t len)
@@ -711,6 +817,7 @@ int ec_key_simple_oct2priv(EC_KEY *eckey, const unsigned char *buf, size_t len)
         ECerr(EC_F_EC_KEY_SIMPLE_OCT2PRIV, ERR_R_BN_LIB);
         return 0;
     }
+    eckey->dirty_cnt++;
     return 1;
 }
 
diff --git a/crypto/ec/ec_local.h b/crypto/ec/ec_local.h
index c0eacc9ce5..dacb2ca0af 100644
--- a/crypto/ec/ec_local.h
+++ b/crypto/ec/ec_local.h
@@ -301,6 +301,9 @@ struct ec_key_st {
 #endif
     CRYPTO_RWLOCK *lock;
     OPENSSL_CTX *libctx;
+
+    /* Provider data */
+    size_t dirty_cnt; /* If any key material changes, increment this */
 };
 
 struct ec_point_st {
diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h
index 95dd1c036e..ca1239dfdd 100644
--- a/crypto/evp/evp_local.h
+++ b/crypto/evp/evp_local.h
@@ -78,6 +78,8 @@ struct evp_keymgmt_st {
     OSSL_OP_keymgmt_free_fn *free;
     OSSL_OP_keymgmt_get_params_fn *get_params;
     OSSL_OP_keymgmt_gettable_params_fn *gettable_params;
+    OSSL_OP_keymgmt_set_params_fn *set_params;
+    OSSL_OP_keymgmt_settable_params_fn *settable_params;
 
     /* Key object checking */
     OSSL_OP_keymgmt_query_operation_name_fn *query_operation_name;
@@ -105,6 +107,8 @@ struct evp_keyexch_st {
     OSSL_OP_keyexch_dupctx_fn *dupctx;
     OSSL_OP_keyexch_set_ctx_params_fn *set_ctx_params;
     OSSL_OP_keyexch_settable_ctx_params_fn *settable_ctx_params;
+    OSSL_OP_keyexch_get_ctx_params_fn *get_ctx_params;
+    OSSL_OP_keyexch_gettable_ctx_params_fn *gettable_ctx_params;
 } /* EVP_KEYEXCH */;
 
 struct evp_signature_st {
diff --git a/crypto/evp/exchange.c b/crypto/evp/exchange.c
index 14ed4dbe8e..901081d062 100644
--- a/crypto/evp/exchange.c
+++ b/crypto/evp/exchange.c
@@ -43,7 +43,7 @@ static void *evp_keyexch_from_dispatch(int name_id,
                                        OSSL_PROVIDER *prov)
 {
     EVP_KEYEXCH *exchange = NULL;
-    int fncnt = 0, paramfncnt = 0;
+    int fncnt = 0, sparamfncnt = 0, gparamfncnt = 0;
 
     if ((exchange = evp_keyexch_new(prov)) == NULL) {
         ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
@@ -88,28 +88,44 @@ static void *evp_keyexch_from_dispatch(int name_id,
                 break;
             exchange->dupctx = OSSL_get_OP_keyexch_dupctx(fns);
             break;
+        case OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS:
+            if (exchange->get_ctx_params != NULL)
+                break;
+            exchange->get_ctx_params = OSSL_get_OP_keyexch_get_ctx_params(fns);
+            gparamfncnt++;
+            break;
+        case OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS:
+            if (exchange->gettable_ctx_params != NULL)
+                break;
+            exchange->gettable_ctx_params
+                = OSSL_get_OP_keyexch_gettable_ctx_params(fns);
+            gparamfncnt++;
+            break;
         case OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS:
             if (exchange->set_ctx_params != NULL)
                 break;
             exchange->set_ctx_params = OSSL_get_OP_keyexch_set_ctx_params(fns);
-            paramfncnt++;
+            sparamfncnt++;
             break;
         case OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS:
             if (exchange->settable_ctx_params != NULL)
                 break;
             exchange->settable_ctx_params
                 = OSSL_get_OP_keyexch_settable_ctx_params(fns);
-            paramfncnt++;
+            sparamfncnt++;
             break;
         }
     }
-    if (fncnt != 4 || (paramfncnt != 0 && paramfncnt != 2)) {
+    if (fncnt != 4
+            || (gparamfncnt != 0 && gparamfncnt != 2)
+            || (sparamfncnt != 0 && sparamfncnt != 2)) {
         /*
          * In order to be a consistent set of functions we must have at least
          * a complete set of "exchange" functions: init, derive, newctx,
          * and freectx. The set_ctx_params and settable_ctx_params functions are
          * optional, but if one of them is present then the other one must also
-         * be present. The dupctx and set_peer functions are optional.
+         * be present. Same goes for get_ctx_params and gettable_ctx_params.
+         * The dupctx and set_peer functions are optional.
          */
         EVPerr(EVP_F_EVP_KEYEXCH_FROM_DISPATCH,
                EVP_R_INVALID_PROVIDER_FUNCTIONS);
diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c
index b2395815c8..3fcc073a5a 100644
--- a/crypto/evp/keymgmt_meth.c
+++ b/crypto/evp/keymgmt_meth.c
@@ -38,7 +38,7 @@ static void *keymgmt_from_dispatch(int name_id,
                                    OSSL_PROVIDER *prov)
 {
     EVP_KEYMGMT *keymgmt = NULL;
-    int paramfncnt = 0, importfncnt = 0, exportfncnt = 0;
+    int setparamfncnt = 0, getparamfncnt = 0, importfncnt = 0, exportfncnt = 0;
 
     if ((keymgmt = keymgmt_new()) == NULL) {
         EVP_KEYMGMT_free(keymgmt);
@@ -58,17 +58,30 @@ static void *keymgmt_from_dispatch(int name_id,
             break;
         case OSSL_FUNC_KEYMGMT_GET_PARAMS:
             if (keymgmt->get_params == NULL) {
-                paramfncnt++;
+                getparamfncnt++;
                 keymgmt->get_params = OSSL_get_OP_keymgmt_get_params(fns);
             }
             break;
         case OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS:
             if (keymgmt->gettable_params == NULL) {
-                paramfncnt++;
+                getparamfncnt++;
                 keymgmt->gettable_params =
                     OSSL_get_OP_keymgmt_gettable_params(fns);
             }
             break;
+         case OSSL_FUNC_KEYMGMT_SET_PARAMS:
+            if (keymgmt->set_params == NULL) {
+                setparamfncnt++;
+                keymgmt->set_params = OSSL_get_OP_keymgmt_set_params(fns);
+            }
+            break;
+        case OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS:
+            if (keymgmt->settable_params == NULL) {
+                setparamfncnt++;
+                keymgmt->settable_params =
+                    OSSL_get_OP_keymgmt_settable_params(fns);
+            }
+            break;
         case OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME:
             if (keymgmt->query_operation_name == NULL)
                 keymgmt->query_operation_name =
@@ -119,7 +132,8 @@ static void *keymgmt_from_dispatch(int name_id,
     if (keymgmt->free == NULL
         || keymgmt->new == NULL
         || keymgmt->has == NULL
-        || (paramfncnt != 0 && paramfncnt != 2)
+        || (getparamfncnt != 0 && getparamfncnt != 2)
+        || (setparamfncnt != 0 && setparamfncnt != 2)
         || (importfncnt != 0 && importfncnt != 2)
         || (exportfncnt != 0 && exportfncnt != 2)) {
         EVP_KEYMGMT_free(keymgmt);
@@ -246,6 +260,21 @@ const OSSL_PARAM *evp_keymgmt_gettable_params(const EVP_KEYMGMT *keymgmt)
     return keymgmt->gettable_params();
 }
 
+int evp_keymgmt_set_params(const EVP_KEYMGMT *keymgmt, void *keydata,
+                           const OSSL_PARAM params[])
+{
+    if (keymgmt->set_params == NULL)
+        return 1;
+    return keymgmt->set_params(keydata, params);
+}
+
+const OSSL_PARAM *evp_keymgmt_settable_params(const EVP_KEYMGMT *keymgmt)
+{
+    if (keymgmt->settable_params == NULL)
+        return NULL;
+    return keymgmt->settable_params();
+}
+
 int evp_keymgmt_has(const EVP_KEYMGMT *keymgmt, void *keydata, int selection)
 {
     /* This is mandatory, no need to check for its presence */
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index 3089d84fa0..f4bc49fe0f 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -570,6 +570,12 @@ int EVP_PKEY_CTX_set_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 #ifndef FIPS_MODE
 int EVP_PKEY_CTX_get_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 {
+    if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
+            && ctx->op.kex.exchprovctx != NULL
+            && ctx->op.kex.exchange != NULL
+            && ctx->op.kex.exchange->get_ctx_params != NULL)
+        return ctx->op.kex.exchange->get_ctx_params(ctx->op.kex.exchprovctx,
+                                                    params);
     if (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
             && ctx->op.sig.sigprovctx != NULL
             && ctx->op.sig.signature != NULL
@@ -587,6 +593,10 @@ int EVP_PKEY_CTX_get_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
 
 const OSSL_PARAM *EVP_PKEY_CTX_gettable_params(EVP_PKEY_CTX *ctx)
 {
+    if (EVP_PKEY_CTX_IS_DERIVE_OP(ctx)
+            && ctx->op.kex.exchange != NULL
+            && ctx->op.kex.exchange->gettable_ctx_params != NULL)
+        return ctx->op.kex.exchange->gettable_ctx_params();
     if (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx)
             && ctx->op.sig.signature != NULL
             && ctx->op.sig.signature->gettable_ctx_params != NULL)
@@ -618,6 +628,52 @@ const OSSL_PARAM *EVP_PKEY_CTX_settable_params(EVP_PKEY_CTX *ctx)
     return NULL;
 }
 
+/*
+ * Internal helpers for stricter EVP_PKEY_CTX_{set,get}_params().
+ *
+ * Return 1 on success, 0 or negative for errors.
+ *
+ * In particular they return -2 if any of the params is not supported.
+ *
+ * They are not available in FIPS_MODE as they depend on
+ *      - EVP_PKEY_CTX_{get,set}_params()
+ *      - EVP_PKEY_CTX_{gettable,settable}_params()
+ *
+ */
+int evp_pkey_ctx_set_params_strict(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
+{
+    const OSSL_PARAM *p;
+
+    if (ctx == NULL || params == NULL)
+        return 0;
+
+    for (p = params; p->key != NULL; p++) {
+        /* Check the ctx actually understands this parameter */
+        if (OSSL_PARAM_locate_const(EVP_PKEY_CTX_settable_params(ctx),
+                                    p->key) == NULL )
+            return -2;
+    }
+
+    return EVP_PKEY_CTX_set_params(ctx, params);
+}
+
+int evp_pkey_ctx_get_params_strict(EVP_PKEY_CTX *ctx, OSSL_PARAM *params)
+{
+    const OSSL_PARAM *p;
+
+    if (ctx == NULL || params == NULL)
+        return 0;
+
+    for (p = params; p->key != NULL; p++ ) {
+        /* Check the ctx actually understands this parameter */
+        if (OSSL_PARAM_locate_const(EVP_PKEY_CTX_gettable_params(ctx),
+                                    p->key) == NULL )
+            return -2;
+    }
+
+    return EVP_PKEY_CTX_get_params(ctx, params);
+}
+
 # ifndef OPENSSL_NO_DH
 int EVP_PKEY_CTX_set_dh_pad(EVP_PKEY_CTX *ctx, int pad)
 {
@@ -713,42 +769,81 @@ int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md)
 static int legacy_ctrl_to_param(EVP_PKEY_CTX *ctx, int keytype, int optype,
                                 int cmd, int p1, void *p2)
 {
-    switch (cmd) {
 # ifndef OPENSSL_NO_DH
-    case EVP_PKEY_CTRL_DH_PAD:
-        return EVP_PKEY_CTX_set_dh_pad(ctx, p1);
+    if (keytype == EVP_PKEY_DH) {
+        switch (cmd) {
+            case EVP_PKEY_CTRL_DH_PAD:
+                return EVP_PKEY_CTX_set_dh_pad(ctx, p1);
+        }
+    }
+# endif
+# ifndef OPENSSL_NO_EC
+    if (keytype == EVP_PKEY_EC) {
+        switch (cmd) {
+            case EVP_PKEY_CTRL_EC_ECDH_COFACTOR:
+                if (p1 == -2) {
+                    return EVP_PKEY_CTX_get_ecdh_cofactor_mode(ctx);
+                } else if (p1 < -1 || p1 > 1) {
+                    /* Uses the same return values as EVP_PKEY_CTX_ctrl */
+                    return -2;
+                } else {
+                    return EVP_PKEY_CTX_set_ecdh_cofactor_mode(ctx, p1);
+                }
+            case EVP_PKEY_CTRL_EC_KDF_TYPE:
+                if (p1 == -2) {
+                    return EVP_PKEY_CTX_get_ecdh_kdf_type(ctx);
+                } else {
+                    return EVP_PKEY_CTX_set_ecdh_kdf_type(ctx, p1);
+                }
+            case EVP_PKEY_CTRL_GET_EC_KDF_MD:
+                return EVP_PKEY_CTX_get_ecdh_kdf_md(ctx, p2);
+            case EVP_PKEY_CTRL_EC_KDF_MD:
+                return EVP_PKEY_CTX_set_ecdh_kdf_md(ctx, p2);
+            case EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN:
+                return EVP_PKEY_CTX_get_ecdh_kdf_outlen(ctx, p2);
+            case EVP_PKEY_CTRL_EC_KDF_OUTLEN:
+                return EVP_PKEY_CTX_set_ecdh_kdf_outlen(ctx, p1);
+            case EVP_PKEY_CTRL_GET_EC_KDF_UKM:
+                return EVP_PKEY_CTX_get0_ecdh_kdf_ukm(ctx, p2);
+            case EVP_PKEY_CTRL_EC_KDF_UKM:
+                return EVP_PKEY_CTX_set0_ecdh_kdf_ukm(ctx, p2, p1);
+        }
+    }
 # endif
-    case EVP_PKEY_CTRL_MD:
-        return EVP_PKEY_CTX_set_signature_md(ctx, p2);
-    case EVP_PKEY_CTRL_GET_MD:
-        return EVP_PKEY_CTX_get_signature_md(ctx, p2);
-    case EVP_PKEY_CTRL_RSA_PADDING:
-        return EVP_PKEY_CTX_set_rsa_padding(ctx, p1);
-    case EVP_PKEY_CTRL_GET_RSA_PADDING:
-        return EVP_PKEY_CTX_get_rsa_padding(ctx, p2);
-    case EVP_PKEY_CTRL_RSA_OAEP_MD:
-        return EVP_PKEY_CTX_set_rsa_oaep_md(ctx, p2);
-    case EVP_PKEY_CTRL_GET_RSA_OAEP_MD:
-        return EVP_PKEY_CTX_get_rsa_oaep_md(ctx, p2);
-    case EVP_PKEY_CTRL_RSA_MGF1_MD:
-        return EVP_PKEY_CTX_set_rsa_oaep_md(ctx, p2);
-    case EVP_PKEY_CTRL_GET_RSA_MGF1_MD:
-        return EVP_PKEY_CTX_get_rsa_oaep_md(ctx, p2);
-    case EVP_PKEY_CTRL_RSA_OAEP_LABEL:
-        return EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, p2, p1);
-    case EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL:
-        return EVP_PKEY_CTX_get0_rsa_oaep_label(ctx, (unsigned char **)p2);
-    case EVP_PKEY_CTRL_PKCS7_ENCRYPT:
-    case EVP_PKEY_CTRL_PKCS7_DECRYPT:
+    if (keytype == -1) {
+        switch (cmd) {
+            case EVP_PKEY_CTRL_MD:
+                return EVP_PKEY_CTX_set_signature_md(ctx, p2);
+            case EVP_PKEY_CTRL_GET_MD:
+                return EVP_PKEY_CTX_get_signature_md(ctx, p2);
+            case EVP_PKEY_CTRL_RSA_PADDING:
+                return EVP_PKEY_CTX_set_rsa_padding(ctx, p1);
+            case EVP_PKEY_CTRL_GET_RSA_PADDING:
+                return EVP_PKEY_CTX_get_rsa_padding(ctx, p2);
+            case EVP_PKEY_CTRL_RSA_OAEP_MD:
+                return EVP_PKEY_CTX_set_rsa_oaep_md(ctx, p2);
+            case EVP_PKEY_CTRL_GET_RSA_OAEP_MD:
+                return EVP_PKEY_CTX_get_rsa_oaep_md(ctx, p2);
+            case EVP_PKEY_CTRL_RSA_MGF1_MD:
+                return EVP_PKEY_CTX_set_rsa_oaep_md(ctx, p2);
+            case EVP_PKEY_CTRL_GET_RSA_MGF1_MD:
+                return EVP_PKEY_CTX_get_rsa_oaep_md(ctx, p2);
+            case EVP_PKEY_CTRL_RSA_OAEP_LABEL:
+                return EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, p2, p1);
+            case EVP_PKEY_CTRL_GET_RSA_OAEP_LABEL:
+                return EVP_PKEY_CTX_get0_rsa_oaep_label(ctx, (unsigned char **)p2);
+            case EVP_PKEY_CTRL_PKCS7_ENCRYPT:
+            case EVP_PKEY_CTRL_PKCS7_DECRYPT:
 # ifndef OPENSSL_NO_CMS
-    case EVP_PKEY_CTRL_CMS_DECRYPT:
-    case EVP_PKEY_CTRL_CMS_ENCRYPT:
+            case EVP_PKEY_CTRL_CMS_DECRYPT:
+            case EVP_PKEY_CTRL_CMS_ENCRYPT:
 # endif
-        if (ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
-            return 1;
-        ERR_raise(ERR_LIB_EVP,
-                  EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
-        return -2;
+                if (ctx->pmeth->pkey_id != EVP_PKEY_RSA_PSS)
+                    return 1;
+                ERR_raise(ERR_LIB_EVP,
+                          EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+                return -2;
+        }
     }
     return 0;
 }
@@ -821,6 +916,12 @@ static int legacy_ctrl_str_to_param(EVP_PKEY_CTX *ctx, const char *name,
     else if (strcmp(name, "dh_pad") == 0)
         name = OSSL_EXCHANGE_PARAM_PAD;
 # endif
+# ifndef OPENSSL_NO_EC
+    else if (strcmp(name, "ecdh_cofactor_mode") == 0)
+        name = OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE;
+    else if (strcmp(name, "ecdh_kdf_md") == 0)
+        name = OSSL_EXCHANGE_PARAM_KDF_TYPE;
+# endif
 
     {
         /*
diff --git a/doc/man7/provider-keyexch.pod b/doc/man7/provider-keyexch.pod
index ceb03ec2f0..3fde04869d 100644
--- a/doc/man7/provider-keyexch.pod
+++ b/doc/man7/provider-keyexch.pod
@@ -31,6 +31,8 @@ provider-keyexch - The keyexch library E<lt>-E<gt> provider functions
  /* Key Exchange parameters */
  int OP_keyexch_set_ctx_params(void *ctx, const OSSL_PARAM params[]);
  const OSSL_PARAM *OP_keyexch_settable_ctx_params(void);
+ int OP_keyexch_get_ctx_params(void *ctx, OSSL_PARAM params[]);
+ const OSSL_PARAM *OP_keyexch_gettable_ctx_params(void);
 
 =head1 DESCRIPTION
 
@@ -71,6 +73,8 @@ macros in L<openssl-core_numbers.h(7)>, as follows:
 
  OP_keyexch_set_ctx_params        OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS
  OP_keyexch_settable_ctx_params   OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS
+ OP_keyexch_get_ctx_params        OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS
+ OP_keyexch_gettable_ctx_params   OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS
 
 A key exchange algorithm implementation may not implement all of these functions.
 In order to be a consistent set of functions a provider must implement
@@ -123,15 +127,36 @@ The length of the shared secret should be written to I<*secretlen>.
 If I<secret> is NULL then the maximum length of the shared secret should be
 written to I<*secretlen>.
 
-=head2 Key Exchange Parameters
-
-See L<OSSL_PARAM(3)> for further details on the parameters structure used by
-the OP_keyexch_set_params() function.
+=head2 Key Exchange Parameters Functions
 
 OP_keyexch_set_ctx_params() sets key exchange parameters associated with the
-given provider side key exchange context I<ctx> to I<params>.
+given provider side key exchange context I<ctx> to I<params>,
+see L</Key Exchange Parameters>.
 Any parameter settings are additional to any that were previously set.
 
+OP_keyexch_get_ctx_params() gets key exchange parameters associated with the
+given provider side key exchange context I<ctx> into I<params>,
+see L</Key Exchange Parameters>.
+
+OP_keyexch_settable_ctx_params() yields a constant B<OSSL_PARAM> array that
+describes the settable parameters, i.e. parameters that can be used with
+OP_signature_set_ctx_params().
+If OP_keyexch_settable_ctx_params() is present, OP_keyexch_set_ctx_params() must
+also be present, and vice versa.
+Similarly, OP_keyexch_gettable_ctx_params() yields a constant B<OSSL_PARAM>
+array that describes the gettable parameters, i.e. parameters that can be
+handled by OP_signature_get_ctx_params().
+If OP_keyexch_gettable_ctx_params() is present, OP_keyexch_get_ctx_params() must
+also be present, and vice versa.
+See L<OSSL_PARAM(3)> for the use of B<OSSL_PARAM> as parameter descriptor.
+
+Notice that not all settable parameters are also gettable, and vice versa.
+
+=head2 Key Exchange Parameters
+
+See L<OSSL_PARAM(3)> for further details on the parameters structure used by
+the OP_keyexch_set_ctx_params() and OP_keyexch_get_ctx_params() functions.
+
 Parameters currently recognised by built-in key exchange algorithms are as
 follows.
 Not all parameters are relevant to, or are understood by all key exchange
@@ -150,20 +175,69 @@ If padding is on then the derived shared secret will have its first bytes filled
 with 0s where necessary to make the shared secret the same size as the largest
 possible secret size.
 
-=back
+=item "ecdh-cofactor-mode" (B<OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE>) <integer>
 
-OP_keyexch_settable_ctx_params() gets a constant B<OSSL_PARAM> array that
-describes the settable parameters, i.e. parameters that can be used with
-OP_signature_set_ctx_params().
-See L<OSSL_PARAM(3)> for the use of B<OSSL_PARAM> as parameter descriptor.
+Sets/gets the ECDH mode of operation for the associated key exchange ctx.
+
+In the context of an Elliptic Curve Diffie-Hellman key exchange, this parameter
+can be used to select between the plain Diffie-Hellman (DH) or Cofactor
+Diffie-Hellman (CDH) variants of the key exchange algorithm.
+
+When setting, the value should be 1, 0 or -1, respectively forcing cofactor mode
+on, off, or resetting it to the default for the private key associated with the
+given key exchange ctx.
+
+When getting, the value should be either 1 or 0, respectively signaling if the
+cofactor mode is on or off.
+
+See also L<provider-keymgmt(7)> for the related
+B<OSSL_PKEY_PARAM_USE_COFACTOR_ECDH> parameter that can be set on a
+per-key basis.
+
+=item "kdf-type" (B<OSSL_EXCHANGE_PARAM_KDF_TYPE>) <utf8_string>
+
+Sets/gets the Key Derivation Function type to apply within the associated key
+exchange ctx.
+
+=item "kdf-digest" (B<OSSL_EXCHANGE_PARAM_KDF_DIGEST>) <utf8_string>
+
+Sets/gets the Digest algorithm to be used as part of the Key Derivation Function
+associated with the given key exchange ctx.
+
+=item "kdf-digest-props" (B<OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS>) <utf8_string>
+
+Sets properties to be used upon look up of the implementation for the selected
+Digest algorithm for the Key Derivation Function associated with the given key
+exchange ctx.
+
+=item "kdf-outlen" (B<OSSL_EXCHANGE_PARAM_KDF_OUTLEN>) <size_t>
+
+Sets/gets the desired size for the output of the chosen Key Derivation Function
+associated with the given key exchange ctx.
+
+=item "kdf-ukm" (B<OSSL_EXCHANGE_PARAM_KDF_UKM>) <octet_string>
+
+Sets/gets User Key Material to be used as part of the selected Key Derivation
+Function associated with the given key exchange ctx.
+
+=item "kdf-ukm-len" (B<OSSL_EXCHANGE_PARAM_KDF_UKM_LEN>) <size_t>
+
+Sets/gets the size of the User Key Material to be used as part of the selected
+Key Derivation Function associated with the given key exchange ctx.
+
+=back
 
 =head1 RETURN VALUES
 
 OP_keyexch_newctx() and OP_keyexch_dupctx() should return the newly created
 provider side key exchange context, or NULL on failure.
 
-OP_keyexch_init(), OP_keyexch_set_peer(), OP_keyexch_derive() and
-OP_keyexch_set_params() should return 1 for success or 0 on error.
+OP_keyexch_init(), OP_keyexch_set_peer(), OP_keyexch_derive(),
+OP_keyexch_set_params(), and OP_keyexch_get_params() should return 1 for success
+or 0 on error.
+
+OP_keyexch_settable_ctx_params() and OP_keyexch_gettable_ctx_params() should
+always return a constant B<OSSL_PARAM> array.
 
 =head1 SEE ALSO
 
diff --git a/doc/man7/provider-keymgmt.pod b/doc/man7/provider-keymgmt.pod
index e9e4de5622..279256d567 100644
--- a/doc/man7/provider-keymgmt.pod
+++ b/doc/man7/provider-keymgmt.pod
@@ -21,6 +21,8 @@ provider-keymgmt - The KEYMGMT library E<lt>-E<gt> provider functions
  /* Key object information */
  int OP_keymgmt_get_params(void *keydata, OSSL_PARAM params[]);
  const OSSL_PARAM *OP_keymgmt_gettable_params(void);
+ int OP_keymgmt_set_params(void *keydata, const OSSL_PARAM params[]);
+ const OSSL_PARAM *OP_keymgmt_settable_params(void);
 
  /* Key object content checks */
  int OP_keymgmt_has(void *keydata, int selection);
@@ -30,7 +32,7 @@ provider-keymgmt - The KEYMGMT library E<lt>-E<gt> provider functions
 
  /* Key object import and export functions */
  int OP_keymgmt_import(int selection, void *keydata, const OSSL_PARAM params[]);
- const OSSL_PARAM *OP_keymgmt_import_types, (int selection);
+ const OSSL_PARAM *OP_keymgmt_import_types(int selection);
  int OP_keymgmt_export(int selection, void *keydata,
                        OSSL_CALLBACK *param_cb, void *cbarg);
  const OSSL_PARAM *OP_keymgmt_export_types(int selection);
@@ -38,9 +40,6 @@ provider-keymgmt - The KEYMGMT library E<lt>-E<gt> provider functions
  /* Key object validation */
  int OP_keymgmt_validate(void *keydata, int selection);
 
- /* Discovery of supported operations */
- const char *OP_keymgmt_query_operation_name(int operation_id);
-
 =head1 DESCRIPTION
 
 The KEYMGMT operation doesn't have much public visibility in OpenSSL
@@ -78,6 +77,8 @@ macros in L<openssl-core_numbers.h(7)>, as follows:
 
  OP_keymgmt_get_params           OSSL_FUNC_KEYMGMT_GET_PARAMS
  OP_keymgmt_gettable_params      OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS
+ OP_keymgmt_set_params           OSSL_FUNC_KEYMGMT_SET_PARAMS
+ OP_keymgmt_settable_params      OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS
 
  OP_keymgmt_query_operation_name OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME
 
@@ -202,7 +203,17 @@ descriptor B<OSSL_PARAM>, for parameters that OP_keymgmt_get_params()
 can handle.
 
 If OP_keymgmt_gettable_params() is present, OP_keymgmt_get_params()
-must also be present.
+must also be present, and vice versa.
+
+OP_keymgmt_set_params() should update information data associated
+with the given I<keydata>, see L</Information Parameters>.
+
+OP_keymgmt_settable_params() should return a constant array of
+descriptor B<OSSL_PARAM>, for parameters that OP_keymgmt_set_params()
+can handle.
+
+If OP_keymgmt_settable_params() is present, OP_keymgmt_set_params()
+must also be present, and vice versa.
 
 =head2 Key Object Checking Functions
 
@@ -214,7 +225,7 @@ returns NULL, the caller is free to assume that there's an algorithm
 from the same provider, of the same name as the one used to fetch the
 keymgmt and try to use that.
 
-OP_keymgmt_has() should check whether the given I<keydata> the subsets
+OP_keymgmt_has() should check whether the given I<keydata> contains the subsets
 of data indicated by the I<selector>.  A combination of several
 selector bits must consider all those subsets, not just one.  An
 implementation is, however, free to consider an empty subset of data
@@ -249,8 +260,10 @@ OP_keymgmt_export() callback can expect to receive.
 
 See L<OSSL_PARAM(3)> for further details on the parameters structure.
 
-Parameters currently recognised by built-in keymgmt algorithms'
-OP_keymgmt_get_params:
+Parameters currently recognised by built-in keymgmt algorithms
+are as follows.
+Not all parameters are relevant to, or are understood by all keymgmt
+algorithms:
 
 =over 4
 
@@ -278,8 +291,46 @@ dimensions handled in the rest of the same provider.
 The value should be the number of security bits of the given key.
 Bits of security is defined in SP800-57.
 
+=item "use-cofactor-flag" (B<OSSL_PKEY_PARAM_USE_COFACTOR_FLAG>,
+B<OSSL_PKEY_PARAM_USE_COFACTOR_ECDH>) <integer>
+
+The value should be either 1 or 0, to respectively enable or disable
+use of the cofactor in operations using this key.
+
+In the context of a key that can be used to perform an Elliptic Curve
+Diffie-Hellman key exchange, this parameter can be used to mark a requirement
+for using the Cofactor Diffie-Hellman (CDH) variant of the key exchange
+algorithm.
+
+See also L<provider-keyexch(7)> for the related
+B<OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE> parameter that can be set on a
+per-operation basis.
+
 =back
 
+=head1 RETURN VALUES
+
+OP_keymgmt_new() should return a valid reference to the newly created provider
+side key object, or NULL on failure.
+
+OP_keymgmt_import(), OP_keymgmt_export(), OP_keymgmt_get_params() and
+OP_keymgmt_set_params() should return 1 for success or 0 on error.
+
+OP_keymgmt_validate() should return 1 on successful validation, or 0 on
+failure.
+
+OP_keymgmt_has() should return 1 if all the selected data subsets are contained
+in the given I<keydata> or 0 otherwise.
+
+OP_keymgmt_query_operation_name() should return a pointer to a string matching
+the requested operation, or NULL if the same name used to fetch the keymgmt
+applies.
+
+OP_keymgmt_gettable_params() and OP_keymgmt_settable_params()
+OP_keymgmt_import_types(), OP_keymgmt_export_types()
+should
+always return a constant B<OSSL_PARAM> array.
+
 =head1 SEE ALSO
 
 L<provider(7)>
diff --git a/include/crypto/evp.h b/include/crypto/evp.h
index 65889ae812..0f5e86b28e 100644
--- a/include/crypto/evp.h
+++ b/include/crypto/evp.h
@@ -594,6 +594,9 @@ void evp_keymgmt_freedata(const EVP_KEYMGMT *keymgmt, void *keyddata);
 int evp_keymgmt_get_params(const EVP_KEYMGMT *keymgmt,
                            void *keydata, OSSL_PARAM params[]);
 const OSSL_PARAM *evp_keymgmt_gettable_params(const EVP_KEYMGMT *keymgmt);
+int evp_keymgmt_set_params(const EVP_KEYMGMT *keymgmt,
+                           void *keydata, const OSSL_PARAM params[]);
+const OSSL_PARAM *evp_keymgmt_settable_params(const EVP_KEYMGMT *keymgmt);
 
 
 int evp_keymgmt_has(const EVP_KEYMGMT *keymgmt, void *keyddata, int selection);
@@ -627,3 +630,19 @@ void evp_encode_ctx_set_flags(EVP_ENCODE_CTX *ctx, unsigned int flags);
 const EVP_CIPHER *evp_get_cipherbyname_ex(OPENSSL_CTX *libctx, const char *name);
 const EVP_MD *evp_get_digestbyname_ex(OPENSSL_CTX *libctx, const char *name);
 
+#ifndef FIPS_MODE
+/*
+ * Internal helpers for stricter EVP_PKEY_CTX_{set,get}_params().
+ *
+ * Return 1 on success, 0 or negative for errors.
+ *
+ * In particular they return -2 if any of the params is not supported.
+ *
+ * They are not available in FIPS_MODE as they depend on
+ *      - EVP_PKEY_CTX_{get,set}_params()
+ *      - EVP_PKEY_CTX_{gettable,settable}_params()
+ *
+ */
+int evp_pkey_ctx_set_params_strict(EVP_PKEY_CTX *ctx, OSSL_PARAM *params);
+int evp_pkey_ctx_get_params_strict(EVP_PKEY_CTX *ctx, OSSL_PARAM *params);
+#endif /* !defined(FIPS_MODE) */
diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h
index 84fbcf38e3..b2befd8bb5 100644
--- a/include/openssl/core_names.h
+++ b/include/openssl/core_names.h
@@ -179,6 +179,14 @@ extern "C" {
 #define OSSL_PKEY_PARAM_FFC_G        "g"
 #define OSSL_PKEY_PARAM_FFC_Q        "q"
 
+/* Elliptic Curve Domain Parameters */
+#define OSSL_PKEY_PARAM_EC_NAME      "curve-name"
+
+/* Elliptic Curve Key Parameters */
+#define OSSL_PKEY_PARAM_USE_COFACTOR_FLAG "use-cofactor-flag"
+#define OSSL_PKEY_PARAM_USE_COFACTOR_ECDH \
+    OSSL_PKEY_PARAM_USE_COFACTOR_FLAG
+
 /* RSA Keys */
 /*
  * n, e, d are the usual public and private key components
@@ -202,7 +210,27 @@ extern "C" {
 
 /* Key Exchange parameters */
 
-#define OSSL_EXCHANGE_PARAM_PAD      "pad" /* uint */
+#define OSSL_EXCHANGE_PARAM_PAD                   "pad" /* uint */
+#define OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE "ecdh-cofactor-mode" /* int */
+#define OSSL_EXCHANGE_PARAM_KDF_TYPE              "kdf-type" /* utf8_string */
+#define OSSL_EXCHANGE_PARAM_KDF_DIGEST            "kdf-digest" /* utf8_string */
+#define OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS      "kdf-digest-props" /* utf8_string */
+#define OSSL_EXCHANGE_PARAM_KDF_OUTLEN            "kdf-outlen" /* size_t */
+
+/*
+ * TODO(3.0): improve this pattern
+ *
+ * Currently the sole internal user of OSSL_EXCHANGE_PARAM_KDF_UKM is
+ * EVP_PKEY_CTX_{set0,get0}_ecdh_kdf_ukm():
+ *      OSSL_EXCHANGE_PARAM_KDF_UKM is handled as a octet_string on set0,
+ *      and as an octet_ptr on get0.
+ *
+ * This pattern is borrowed from the handling of
+ * OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL in
+ * EVP_PKEY_CTX_{set0,get0}_rsa_oaep_label().
+ */
+#define OSSL_EXCHANGE_PARAM_KDF_UKM               "kdf-ukm" /* see note above */
+#define OSSL_EXCHANGE_PARAM_KDF_UKM_LEN           "kdf-ukm-len" /* size_t */
 
 /* Signature parameters */
 #define OSSL_SIGNATURE_PARAM_ALGORITHM_ID       "algorithm-id"
diff --git a/include/openssl/core_numbers.h b/include/openssl/core_numbers.h
index 547aeb5858..3fd462a8d6 100644
--- a/include/openssl/core_numbers.h
+++ b/include/openssl/core_numbers.h
@@ -393,6 +393,12 @@ OSSL_CORE_MAKE_FUNC(int, OP_keymgmt_get_params,
                     (void *keydata, OSSL_PARAM params[]))
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_gettable_params, (void))
 
+#define OSSL_FUNC_KEYMGMT_SET_PARAMS                  12
+#define OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS             13
+OSSL_CORE_MAKE_FUNC(int, OP_keymgmt_set_params,
+                    (void *keydata, const OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_settable_params, (void))
+
 /* Key checks - discovery of supported operations */
 # define OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME       20
 OSSL_CORE_MAKE_FUNC(const char *, OP_keymgmt_query_operation_name,
@@ -431,6 +437,8 @@ OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_export_types,
 # define OSSL_FUNC_KEYEXCH_DUPCTX                      6
 # define OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS              7
 # define OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS         8
+# define OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS              9
+# define OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS        10
 
 OSSL_CORE_MAKE_FUNC(void *, OP_keyexch_newctx, (void *provctx))
 OSSL_CORE_MAKE_FUNC(int, OP_keyexch_init, (void *ctx, void *provkey))
@@ -443,6 +451,10 @@ OSSL_CORE_MAKE_FUNC(int, OP_keyexch_set_ctx_params, (void *ctx,
                                                      const OSSL_PARAM params[]))
 OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keyexch_settable_ctx_params,
                     (void))
+OSSL_CORE_MAKE_FUNC(int, OP_keyexch_get_ctx_params, (void *ctx,
+                                                     OSSL_PARAM params[]))
+OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keyexch_gettable_ctx_params,
+                    (void))
 
 /* Signature */
 
diff --git a/include/openssl/ec.h b/include/openssl/ec.h
index 0cca244a67..c5d5fc04fe 100644
--- a/include/openssl/ec.h
+++ b/include/openssl/ec.h
@@ -1460,55 +1460,21 @@ DEPRECATEDIN_3_0(void EC_KEY_METHOD_get_verify
                           EVP_PKEY_OP_PARAMGEN|EVP_PKEY_OP_KEYGEN, \
                           EVP_PKEY_CTRL_EC_PARAM_ENC, flag, NULL)
 
-#  define EVP_PKEY_CTX_set_ecdh_cofactor_mode(ctx, flag) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_EC_ECDH_COFACTOR, flag, NULL)
-
-#  define EVP_PKEY_CTX_get_ecdh_cofactor_mode(ctx) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_EC_ECDH_COFACTOR, -2, NULL)
-
-#  define EVP_PKEY_CTX_set_ecdh_kdf_type(ctx, kdf) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_EC_KDF_TYPE, kdf, NULL)
+int EVP_PKEY_CTX_set_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx, int cofactor_mode);
+int EVP_PKEY_CTX_get_ecdh_cofactor_mode(EVP_PKEY_CTX *ctx);
 
-#  define EVP_PKEY_CTX_get_ecdh_kdf_type(ctx) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_EC_KDF_TYPE, -2, NULL)
+int EVP_PKEY_CTX_set_ecdh_kdf_type(EVP_PKEY_CTX *ctx, int kdf);
+int EVP_PKEY_CTX_get_ecdh_kdf_type(EVP_PKEY_CTX *ctx);
 
-#  define EVP_PKEY_CTX_set_ecdh_kdf_md(ctx, md) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_EC_KDF_MD, 0, (void *)(md))
+int EVP_PKEY_CTX_set_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD *md);
+int EVP_PKEY_CTX_get_ecdh_kdf_md(EVP_PKEY_CTX *ctx, const EVP_MD **md);
 
-#  define EVP_PKEY_CTX_get_ecdh_kdf_md(ctx, pmd) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_GET_EC_KDF_MD, 0, (void *)(pmd))
+int EVP_PKEY_CTX_set_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int len);
+int EVP_PKEY_CTX_get_ecdh_kdf_outlen(EVP_PKEY_CTX *ctx, int *len);
 
-#  define EVP_PKEY_CTX_set_ecdh_kdf_outlen(ctx, len) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_EC_KDF_OUTLEN, len, NULL)
-
-#  define EVP_PKEY_CTX_get_ecdh_kdf_outlen(ctx, plen) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_GET_EC_KDF_OUTLEN, 0, (void *)(plen))
-
-#  define EVP_PKEY_CTX_set0_ecdh_kdf_ukm(ctx, p, plen) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_EC_KDF_UKM, plen, (void *)(p))
-
-#  define EVP_PKEY_CTX_get0_ecdh_kdf_ukm(ctx, p) \
-        EVP_PKEY_CTX_ctrl(ctx, EVP_PKEY_EC, \
-                          EVP_PKEY_OP_DERIVE, \
-                          EVP_PKEY_CTRL_GET_EC_KDF_UKM, 0, (void *)(p))
+int EVP_PKEY_CTX_set0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char *ukm,
+                                   int len);
+int EVP_PKEY_CTX_get0_ecdh_kdf_ukm(EVP_PKEY_CTX *ctx, unsigned char **ukm);
 
 /* SM2 will skip the operation check so no need to pass operation here */
 #  define EVP_PKEY_CTX_set1_id(ctx, id, id_len) \
diff --git a/providers/defltprov.c b/providers/defltprov.c
index 6740a8e58f..3819c61659 100644
--- a/providers/defltprov.c
+++ b/providers/defltprov.c
@@ -373,6 +373,7 @@ static const OSSL_ALGORITHM deflt_keyexch[] = {
     { "DH:dhKeyAgreement", "default=yes", dh_keyexch_functions },
 #endif
 #ifndef OPENSSL_NO_EC
+    { "ECDH:id-ecPublicKey", "default=yes", ecdh_keyexch_functions },
     { "X25519", "default=yes", x25519_keyexch_functions },
     { "X448", "default=yes", x448_keyexch_functions },
 #endif
@@ -400,6 +401,7 @@ static const OSSL_ALGORITHM deflt_keymgmt[] = {
 #endif
     { "RSA:rsaEncryption", "default=yes", rsa_keymgmt_functions },
 #ifndef OPENSSL_NO_EC
+    { "EC:id-ecPublicKey", "default=yes", ec_keymgmt_functions },
     { "X25519", "default=yes", x25519_keymgmt_functions },
     { "X448", "default=yes", x448_keymgmt_functions },
 #endif
diff --git a/providers/implementations/exchange/build.info b/providers/implementations/exchange/build.info
index 51d32fc090..82b688def3 100644
--- a/providers/implementations/exchange/build.info
+++ b/providers/implementations/exchange/build.info
@@ -3,6 +3,7 @@
 
 $DH_GOAL=../../libimplementations.a
 $ECX_GOAL=../../libimplementations.a
+$ECDH_GOAL=../../libimplementations.a
 
 IF[{- !$disabled{dh} -}]
   SOURCE[$DH_GOAL]=dh_exch.c
@@ -21,4 +22,5 @@ ENDIF
 IF[{- !$disabled{ec} -}]
   SOURCE[$ECX_GOAL]=ecx_exch.c
   DEFINE[$ECX_GOAL]=$ECDEF
+  SOURCE[$ECDH_GOAL]=ecdh_exch.c
 ENDIF
diff --git a/providers/implementations/exchange/ecdh_exch.c b/providers/implementations/exchange/ecdh_exch.c
new file mode 100644
index 0000000000..bf353fa175
--- /dev/null
+++ b/providers/implementations/exchange/ecdh_exch.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright 2020 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
+ */
+
+/*
+ * ECDH low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_numbers.h>
+#include <openssl/core_names.h>
+#include <openssl/ec.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "crypto/ec.h" /* ecdh_KDF_X9_63() */
+
+static OSSL_OP_keyexch_newctx_fn ecdh_newctx;
+static OSSL_OP_keyexch_init_fn ecdh_init;
+static OSSL_OP_keyexch_set_peer_fn ecdh_set_peer;
+static OSSL_OP_keyexch_derive_fn ecdh_derive;
+static OSSL_OP_keyexch_freectx_fn ecdh_freectx;
+static OSSL_OP_keyexch_dupctx_fn ecdh_dupctx;
+static OSSL_OP_keyexch_set_ctx_params_fn ecdh_set_ctx_params;
+static OSSL_OP_keyexch_settable_ctx_params_fn ecdh_settable_ctx_params;
+static OSSL_OP_keyexch_get_ctx_params_fn ecdh_get_ctx_params;
+static OSSL_OP_keyexch_gettable_ctx_params_fn ecdh_gettable_ctx_params;
+
+enum kdf_type {
+    PROV_ECDH_KDF_NONE = 0,
+    PROV_ECDH_KDF_X9_63
+};
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes EC_KEY structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+    OPENSSL_CTX *libctx;
+
+    EC_KEY *k;
+    EC_KEY *peerk;
+
+    /*
+     * ECDH cofactor mode:
+     *
+     *  . 0  disabled
+     *  . 1  enabled
+     *  . -1 use cofactor mode set for k
+     */
+    int cofactor_mode;
+
+    /************
+     * ECDH KDF *
+     ************/
+    /* KDF (if any) to use for ECDH */
+    enum kdf_type kdf_type;
+    /* Message digest to use for key derivation */
+    EVP_MD *kdf_md;
+    /* User key material */
+    unsigned char *kdf_ukm;
+    size_t kdf_ukmlen;
+    /* KDF output length */
+    size_t kdf_outlen;
+} PROV_ECDH_CTX;
+
+static
+void *ecdh_newctx(void *provctx)
+{
+    PROV_ECDH_CTX *pectx = OPENSSL_zalloc(sizeof(*pectx));
+
+    if (pectx == NULL)
+        return NULL;
+
+    pectx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
+    pectx->cofactor_mode = -1;
+    pectx->kdf_type = PROV_ECDH_KDF_NONE;
+
+    return (void *)pectx;
+}
+
+static
+int ecdh_init(void *vpecdhctx, void *vecdh)
+{
+    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+    if (pecdhctx == NULL || vecdh == NULL || !EC_KEY_up_ref(vecdh))
+        return 0;
+    EC_KEY_free(pecdhctx->k);
+    pecdhctx->k = vecdh;
+    pecdhctx->cofactor_mode = -1;
+    pecdhctx->kdf_type = PROV_ECDH_KDF_NONE;
+    return 1;
+}
+
+static
+int ecdh_set_peer(void *vpecdhctx, void *vecdh)
+{
+    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+    if (pecdhctx == NULL || vecdh == NULL || !EC_KEY_up_ref(vecdh))
+        return 0;
+    EC_KEY_free(pecdhctx->peerk);
+    pecdhctx->peerk = vecdh;
+    return 1;
+}
+
+static
+void ecdh_freectx(void *vpecdhctx)
+{
+    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+    EC_KEY_free(pecdhctx->k);
+    EC_KEY_free(pecdhctx->peerk);
+
+    EVP_MD_free(pecdhctx->kdf_md);
+    OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);
+
+    OPENSSL_free(pecdhctx);
+}
+
+static
+void *ecdh_dupctx(void *vpecdhctx)
+{
+    PROV_ECDH_CTX *srcctx = (PROV_ECDH_CTX *)vpecdhctx;
+    PROV_ECDH_CTX *dstctx;
+
+    dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+    if (dstctx == NULL)
+        return NULL;
+
+    *dstctx = *srcctx;
+
+    /* clear all pointers */
+
+    dstctx->k= NULL;
+    dstctx->peerk = NULL;
+    dstctx->kdf_md = NULL;
+    dstctx->kdf_ukm = NULL;
+
+    /* up-ref all ref-counted objects referenced in dstctx */
+
+    if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))
+        goto err;
+    else
+        dstctx->k = srcctx->k;
+
+    if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))
+        goto err;
+    else
+        dstctx->peerk = srcctx->peerk;
+
+    if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))
+        goto err;
+    else
+        dstctx->kdf_md = srcctx->kdf_md;
+
+    /* Duplicate UKM data if present */
+    if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {
+        dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,
+                                         srcctx->kdf_ukmlen);
+        if (dstctx->kdf_ukm == NULL)
+            goto err;
+    }
+
+    return dstctx;
+
+ err:
+    ecdh_freectx(dstctx);
+    return NULL;
+}
+
+static
+int ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])
+{
+    char name[80] = { '\0' }; /* should be big enough */
+    char *str = NULL;
+    PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
+    const OSSL_PARAM *p;
+
+    if (pectx == NULL || params == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
+    if (p != NULL) {
+        int mode;
+
+        if (!OSSL_PARAM_get_int(p, &mode))
+            return 0;
+
+        if (mode < -1 || mode > 1)
+            return 0;
+
+        pectx->cofactor_mode = mode;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+    if (p != NULL) {
+        str = name;
+        if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+            return 0;
+
+        if (name[0] == '\0')
+            pectx->kdf_type = PROV_ECDH_KDF_NONE;
+        else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0)
+            pectx->kdf_type = PROV_ECDH_KDF_X9_63;
+        else
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+    if (p != NULL) {
+        char mdprops[80] = { '\0' }; /* should be big enough */
+
+        str = name;
+        if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+            return 0;
+
+        str = mdprops;
+        p = OSSL_PARAM_locate_const(params,
+                                    OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);
+
+        if (p != NULL) {
+            if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
+                return 0;
+        }
+
+        EVP_MD_free(pectx->kdf_md);
+        pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops);
+
+        if (pectx->kdf_md == NULL)
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+    if (p != NULL) {
+        size_t outlen;
+
+        if (!OSSL_PARAM_get_size_t(p, &outlen))
+            return 0;
+        pectx->kdf_outlen = outlen;
+    }
+
+    p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+    if (p != NULL) {
+        void *tmp_ukm = NULL;
+        size_t tmp_ukmlen;
+
+        if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))
+            return 0;
+        OPENSSL_free(pectx->kdf_ukm);
+        pectx->kdf_ukm = tmp_ukm;
+        pectx->kdf_ukmlen = tmp_ukmlen;
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
+    OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ecdh_settable_ctx_params(void)
+{
+    return known_settable_ctx_params;
+}
+
+static
+int ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[])
+{
+    PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
+    OSSL_PARAM *p;
+
+    if (pectx == NULL || params == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
+    if (p != NULL) {
+        int mode = pectx->cofactor_mode;
+
+        if (mode == -1) {
+            /* check what is the default for pecdhctx->k */
+            mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? 1 : 0;
+        }
+
+        if (!OSSL_PARAM_set_int(p, mode))
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+    if (p != NULL) {
+        const char *kdf_type = NULL;
+
+        switch (pectx->kdf_type) {
+            case PROV_ECDH_KDF_NONE:
+                kdf_type = "";
+                break;
+            case PROV_ECDH_KDF_X9_63:
+                kdf_type = OSSL_KDF_NAME_X963KDF;
+                break;
+            default:
+                return 0;
+        }
+
+        if (!OSSL_PARAM_set_utf8_string(p, kdf_type))
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+    if (p != NULL
+            && !OSSL_PARAM_set_utf8_string(p, pectx->kdf_md == NULL
+                                           ? ""
+                                           : EVP_MD_name(pectx->kdf_md))){
+        return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+    if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_outlen))
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+    if (p != NULL && !OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, 0))
+        return 0;
+
+    p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM_LEN);
+    if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_ukmlen))
+        return 0;
+
+    return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+    OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+    OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+    OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,
+                    NULL, 0),
+    OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_UKM_LEN, NULL),
+    OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ecdh_gettable_ctx_params(void)
+{
+    return known_gettable_ctx_params;
+}
+
+static ossl_inline
+size_t ecdh_size(const EC_KEY *k)
+{
+    size_t degree = 0;
+    const EC_GROUP *group;
+
+    if (k == NULL
+            || (group = EC_KEY_get0_group(k)) == NULL)
+        return 0;
+
+    degree = EC_GROUP_get_degree(group);
+
+    return (degree + 7) / 8;
+}
+
+static ossl_inline
+int ecdh_plain_derive(void *vpecdhctx, unsigned char *secret,
+                      size_t *psecretlen, size_t outlen)
+{
+    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+    int retlen, ret = 0;
+    size_t ecdhsize, size;
+    const EC_POINT *ppubkey = NULL;
+    EC_KEY *privk = NULL;
+    const EC_GROUP *group;
+    const BIGNUM *cofactor;
+    int key_cofactor_mode;
+
+    if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) {
+        ERR_raise(ERR_LIB_PROV, EC_R_KEYS_NOT_SET);
+        return 0;
+    }
+
+    ecdhsize = ecdh_size(pecdhctx->k);
+    if (secret == NULL) {
+        *psecretlen = ecdhsize;
+        return 1;
+    }
+
+    if ((group = EC_KEY_get0_group(pecdhctx->k)) == NULL
+            || (cofactor = EC_GROUP_get0_cofactor(group)) == NULL )
+        return 0;
+
+    /*
+     * NB: unlike PKCS#3 DH, if outlen is less than maximum size this is not
+     * an error, the result is truncated.
+     */
+    size = outlen < ecdhsize ? outlen : ecdhsize;
+
+    /*
+     * The ctx->cofactor_mode flag has precedence over the
+     * cofactor_mode flag set on ctx->k.
+     *
+     * - if ctx->cofactor_mode == -1, use ctx->k directly
+     * - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly
+     * - if ctx->cofactor_mode != key_cofactor_mode:
+     *     - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use
+     *          ctx->k directly
+     *     - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag
+     *          set to ctx->cofactor_mode
+     */
+    key_cofactor_mode =
+        (EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
+    if (pecdhctx->cofactor_mode != -1
+            && pecdhctx->cofactor_mode != key_cofactor_mode
+            && !BN_is_one(cofactor)) {
+        if ((privk = EC_KEY_dup(pecdhctx->k)) == NULL)
+            return 0;
+
+        if (pecdhctx->cofactor_mode == 1)
+            EC_KEY_set_flags(privk, EC_FLAG_COFACTOR_ECDH);
+        else
+            EC_KEY_clear_flags(privk, EC_FLAG_COFACTOR_ECDH);
+    } else {
+        privk = pecdhctx->k;
+    }
+
+    ppubkey = EC_KEY_get0_public_key(pecdhctx->peerk);
+
+    retlen = ECDH_compute_key(secret, size, ppubkey, privk, NULL);
+
+    if (retlen <= 0)
+        goto end;
+
+    *psecretlen = retlen;
+    ret = 1;
+
+ end:
+    if (privk != pecdhctx->k)
+        EC_KEY_free(privk);
+    return ret;
+}
+
+static ossl_inline
+int ecdh_X9_63_kdf_derive(void *vpecdhctx, unsigned char *secret,
+                          size_t *psecretlen, size_t outlen)
+{
+    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+    unsigned char *stmp = NULL;
+    size_t stmplen;
+    int ret = 0;
+
+    if (secret == NULL) {
+        *psecretlen = pecdhctx->kdf_outlen;
+        return 1;
+    }
+
+    if (pecdhctx->kdf_outlen > outlen)
+        return 0;
+    if (!ecdh_plain_derive(vpecdhctx, NULL, &stmplen, 0))
+        return 0;
+    if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL) {
+        ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+    if (!ecdh_plain_derive(vpecdhctx, stmp, &stmplen, stmplen))
+        goto err;
+
+    /* Do KDF stuff */
+    if (!ecdh_KDF_X9_63(secret, pecdhctx->kdf_outlen,
+                        stmp, stmplen,
+                        pecdhctx->kdf_ukm,
+                        pecdhctx->kdf_ukmlen,
+                        pecdhctx->kdf_md))
+        goto err;
+    *psecretlen = pecdhctx->kdf_outlen;
+    ret = 1;
+
+ err:
+    OPENSSL_secure_clear_free(stmp, stmplen);
+    return ret;
+}
+
+static
+int ecdh_derive(void *vpecdhctx, unsigned char *secret,
+                size_t *psecretlen, size_t outlen)
+{
+    PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+    switch (pecdhctx->kdf_type) {
+        case PROV_ECDH_KDF_NONE:
+            return ecdh_plain_derive(vpecdhctx, secret, psecretlen, outlen);
+        case PROV_ECDH_KDF_X9_63:
+            return ecdh_X9_63_kdf_derive(vpecdhctx, secret, psecretlen, outlen);
+    }
+
+    return 0;
+}
+
+
+
+const OSSL_DISPATCH ecdh_keyexch_functions[] = {
+    { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdh_newctx },
+    { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdh_init },
+    { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdh_derive },
+    { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdh_set_peer },
+    { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdh_freectx },
+    { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecdh_dupctx },
+    { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))ecdh_set_ctx_params },
+    { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
+      (void (*)(void))ecdh_settable_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecdh_get_ctx_params },
+    { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+      (void (*)(void))ecdh_gettable_ctx_params },
+    { 0, NULL }
+};
diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h
index f4e0dc9b02..ec0507d86b 100644
--- a/providers/implementations/include/prov/implementations.h
+++ b/providers/implementations/include/prov/implementations.h
@@ -259,11 +259,13 @@ extern const OSSL_DISPATCH dsa_keymgmt_functions[];
 extern const OSSL_DISPATCH rsa_keymgmt_functions[];
 extern const OSSL_DISPATCH x25519_keymgmt_functions[];
 extern const OSSL_DISPATCH x448_keymgmt_functions[];
+extern const OSSL_DISPATCH ec_keymgmt_functions[];
 
 /* Key Exchange */
 extern const OSSL_DISPATCH dh_keyexch_functions[];
 extern const OSSL_DISPATCH x25519_keyexch_functions[];
 extern const OSSL_DISPATCH x448_keyexch_functions[];
+extern const OSSL_DISPATCH ecdh_keyexch_functions[];
 
 /* Signature */
 extern const OSSL_DISPATCH dsa_signature_functions[];
diff --git a/providers/implementations/keymgmt/build.info b/providers/implementations/keymgmt/build.info
index 1e4146d15a..89d33e32f0 100644
--- a/providers/implementations/keymgmt/build.info
+++ b/providers/implementations/keymgmt/build.info
@@ -3,6 +3,7 @@
 
 $DH_GOAL=../../libimplementations.a
 $DSA_GOAL=../../libimplementations.a
+$EC_GOAL=../../libimplementations.a
 $RSA_GOAL=../../libimplementations.a
 $ECX_GOAL=../../libimplementations.a
 
@@ -12,6 +13,9 @@ ENDIF
 IF[{- !$disabled{dsa} -}]
   SOURCE[$DSA_GOAL]=dsa_kmgmt.c
 ENDIF
+IF[{- !$disabled{ec} -}]
+  SOURCE[$EC_GOAL]=ec_kmgmt.c
+ENDIF
 SOURCE[$RSA_GOAL]=rsa_kmgmt.c
 IF[{- !$disabled{ec} -}]
   SOURCE[$ECX_GOAL]=ecx_kmgmt.c
diff --git a/providers/implementations/keymgmt/dh_kmgmt.c b/providers/implementations/keymgmt/dh_kmgmt.c
index 1694421c3c..0063324f48 100644
--- a/providers/implementations/keymgmt/dh_kmgmt.c
+++ b/providers/implementations/keymgmt/dh_kmgmt.c
@@ -111,7 +111,7 @@ static int params_to_key(DH *dh, const OSSL_PARAM params[])
     return 1;
 
  err:
-    BN_free(priv_key);
+    BN_clear_free(priv_key);
     BN_free(pub_key);
     return 0;
 }
diff --git a/providers/implementations/keymgmt/dsa_kmgmt.c b/providers/implementations/keymgmt/dsa_kmgmt.c
index 1855474c85..0781f13760 100644
--- a/providers/implementations/keymgmt/dsa_kmgmt.c
+++ b/providers/implementations/keymgmt/dsa_kmgmt.c
@@ -123,7 +123,7 @@ static int params_to_key(DSA *dsa, const OSSL_PARAM params[])
     return 1;
 
  err:
-    BN_free(priv_key);
+    BN_clear_free(priv_key);
     BN_free(pub_key);
     return 0;
 }
diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c
new file mode 100644
index 0000000000..794dd92499
--- /dev/null
+++ b/providers/implementations/keymgmt/ec_kmgmt.c
@@ -0,0 +1,721 @@
+/*
+ * Copyright 2020 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
+ */
+
+/*
+ * ECDH/ECDSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/core_numbers.h>
+#include <openssl/core_names.h>
+#include <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/objects.h>
+#include <openssl/params.h>
+#include "crypto/bn.h"
+#include "internal/param_build.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_OP_keymgmt_new_fn ec_newdata;
+static OSSL_OP_keymgmt_free_fn ec_freedata;
+static OSSL_OP_keymgmt_get_params_fn ec_get_params;
+static OSSL_OP_keymgmt_gettable_params_fn ec_gettable_params;
+static OSSL_OP_keymgmt_set_params_fn ec_set_params;
+static OSSL_OP_keymgmt_settable_params_fn ec_settable_params;
+static OSSL_OP_keymgmt_has_fn ec_has;
+static OSSL_OP_keymgmt_import_fn ec_import;
+static OSSL_OP_keymgmt_import_types_fn ec_import_types;
+static OSSL_OP_keymgmt_export_fn ec_export;
+static OSSL_OP_keymgmt_export_types_fn ec_export_types;
+static OSSL_OP_keymgmt_query_operation_name_fn ec_query_operation_name;
+
+#define EC_POSSIBLE_SELECTIONS                 \
+    (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS )
+
+static
+const char *ec_query_operation_name(int operation_id)
+{
+    switch (operation_id) {
+    case OSSL_OP_KEYEXCH:
+        return "ECDH";
+#if 0
+    case OSSL_OP_SIGNATURE:
+        return deflt_signature;
+#endif
+    }
+    return NULL;
+}
+
+static ossl_inline
+int params_to_domparams(EC_KEY *ec, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *param_ec_name;
+    EC_GROUP *ecg = NULL;
+    char *curve_name = NULL;
+    int ok = 0;
+
+    if (ec == NULL)
+        return 0;
+
+    param_ec_name = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_NAME);
+    if (param_ec_name == NULL) {
+        /* explicit parameters */
+
+        /*
+         * TODO(3.0): should we support explicit parameters curves?
+         */
+        return 0;
+    } else {
+        /* named curve */
+        int curve_nid;
+
+        if (!OSSL_PARAM_get_utf8_string(param_ec_name, &curve_name, 0)
+                || curve_name == NULL
+                || (curve_nid = OBJ_sn2nid(curve_name)) == NID_undef)
+            goto err;
+
+        if ((ecg = EC_GROUP_new_by_curve_name(curve_nid)) == NULL)
+            goto err;
+    }
+
+    if (!EC_KEY_set_group(ec, ecg))
+        goto err;
+
+    /*
+     * TODO(3.0): if the group has changed, should we invalidate the private and
+     * public key?
+     */
+
+    ok = 1;
+
+ err:
+    OPENSSL_free(curve_name);
+    EC_GROUP_free(ecg);
+    return ok;
+}
+
+static ossl_inline
+int domparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl)
+{
+    const EC_GROUP *ecg;
+    int curve_nid;
+
+    if (ec == NULL)
+        return 0;
+
+    ecg = EC_KEY_get0_group(ec);
+    if (ecg == NULL)
+        return 0;
+
+    curve_nid = EC_GROUP_get_curve_name(ecg);
+
+    if (curve_nid == NID_undef) {
+        /* explicit parameters */
+
+        /*
+         * TODO(3.0): should we support explicit parameters curves?
+         */
+        return 0;
+    } else {
+        /* named curve */
+        const char *curve_name = NULL;
+
+        if ((curve_name = OBJ_nid2sn(curve_nid)) == NULL)
+            return 0;
+
+        if (!ossl_param_bld_push_utf8_string(tmpl, OSSL_PKEY_PARAM_EC_NAME, curve_name, 0))
+            return 0;
+    }
+
+    return 1;
+}
+
+/*
+ * Callers of params_to_key MUST make sure that params_to_domparams has been
+ * called before!
+ *
+ * This function only imports the bare keypair, domain parameters and other
+ * parameters are imported separately, and domain parameters are required to
+ * define a keypair.
+ */
+static ossl_inline
+int params_to_key(EC_KEY *ec, const OSSL_PARAM params[], int include_private)
+{
+    const OSSL_PARAM *param_priv_key, *param_pub_key;
+    BIGNUM *priv_key = NULL;
+    unsigned char *pub_key = NULL;
+    size_t pub_key_len;
+    const EC_GROUP *ecg = NULL;
+    EC_POINT *pub_point = NULL;
+    int ok = 0;
+
+    ecg = EC_KEY_get0_group(ec);
+    if (ecg == NULL)
+        return 0;
+
+    param_priv_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+    param_pub_key =
+        OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+
+    /*
+     * We want to have at least a public key either way, so we end up
+     * requiring it unconditionally.
+     */
+    if (param_pub_key == NULL
+            || !OSSL_PARAM_get_octet_string(param_pub_key,
+                                            (void **)&pub_key, 0, &pub_key_len)
+            || (pub_point = EC_POINT_new(ecg)) == NULL
+            || !EC_POINT_oct2point(ecg, pub_point,
+                                   pub_key, pub_key_len, NULL))
+        goto err;
+
+    if (param_priv_key != NULL && include_private) {
+        int fixed_top;
+        const BIGNUM *order;
+
+        /*
+         * Key import/export should never leak the bit length of the secret
+         * scalar in the key.
+         *
+         * For this reason, on export we use padded BIGNUMs with fixed length.
+         *
+         * When importing we also should make sure that, even if short lived,
+         * the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as
+         * soon as possible, so that any processing of this BIGNUM might opt for
+         * constant time implementations in the backend.
+         *
+         * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
+         * to preallocate the BIGNUM internal buffer to a fixed public size big
+         * enough that operations performed during the processing never trigger
+         * a realloc which would leak the size of the scalar through memory
+         * accesses.
+         *
+         * Fixed Length
+         * ------------
+         *
+         * The order of the large prime subgroup of the curve is our choice for
+         * a fixed public size, as that is generally the upper bound for
+         * generating a private key in EC cryptosystems and should fit all valid
+         * secret scalars.
+         *
+         * For padding on export we just use the bit length of the order
+         * converted to bytes (rounding up).
+         *
+         * For preallocating the BIGNUM storage we look at the number of "words"
+         * required for the internal representation of the order, and we
+         * preallocate 2 extra "words" in case any of the subsequent processing
+         * might temporarily overflow the order length.
+         */
+        order = EC_GROUP_get0_order(ecg);
+        if (order == NULL || BN_is_zero(order))
+            goto err;
+
+        fixed_top = bn_get_top(order) + 2;
+
+        if ((priv_key = BN_new()) == NULL)
+            goto err;
+        if (bn_wexpand(priv_key, fixed_top) == NULL)
+            goto err;
+        BN_set_flags(priv_key, BN_FLG_CONSTTIME);
+
+        if (!OSSL_PARAM_get_BN(param_priv_key, &priv_key))
+            goto err;
+    }
+
+    if (priv_key != NULL
+            && !EC_KEY_set_private_key(ec, priv_key))
+        goto err;
+
+    if (!EC_KEY_set_public_key(ec, pub_point))
+        goto err;
+
+    ok = 1;
+
+ err:
+    BN_clear_free(priv_key);
+    OPENSSL_free(pub_key);
+    EC_POINT_free(pub_point);
+    return ok;
+}
+
+/*
+ * Callers of key_to_params MUST make sure that domparams_to_params is also
+ * called!
+ *
+ * This function only exports the bare keypair, domain parameters and other
+ * parameters are exported separately.
+ */
+static ossl_inline
+int key_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl, int include_private)
+{
+    const BIGNUM *priv_key = NULL;
+    const EC_POINT *pub_point = NULL;
+    const EC_GROUP *ecg = NULL;
+    unsigned char *pub_key = NULL;
+    size_t pub_key_len = 0;
+    int ret = 0;
+
+    if (eckey == NULL)
+        return 0;
+
+    ecg = EC_KEY_get0_group(eckey);
+    priv_key = EC_KEY_get0_private_key(eckey);
+    pub_point = EC_KEY_get0_public_key(eckey);
+
+    /* group and public_key must be present, priv_key is optional */
+    if (ecg == NULL || pub_point == NULL)
+        return 0;
+    if ((pub_key_len = EC_POINT_point2buf(ecg, pub_point,
+                                          POINT_CONVERSION_COMPRESSED,
+                                          &pub_key, NULL)) == 0)
+        return 0;
+
+    if (!ossl_param_bld_push_octet_string(tmpl,
+                                          OSSL_PKEY_PARAM_PUB_KEY,
+                                          pub_key, pub_key_len))
+        goto err;
+
+    if (priv_key != NULL && include_private) {
+        size_t sz;
+        int ecbits;
+
+        /*
+         * Key import/export should never leak the bit length of the secret
+         * scalar in the key.
+         *
+         * For this reason, on export we use padded BIGNUMs with fixed length.
+         *
+         * When importing we also should make sure that, even if short lived,
+         * the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as
+         * soon as possible, so that any processing of this BIGNUM might opt for
+         * constant time implementations in the backend.
+         *
+         * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
+         * to preallocate the BIGNUM internal buffer to a fixed public size big
+         * enough that operations performed during the processing never trigger
+         * a realloc which would leak the size of the scalar through memory
+         * accesses.
+         *
+         * Fixed Length
+         * ------------
+         *
+         * The order of the large prime subgroup of the curve is our choice for
+         * a fixed public size, as that is generally the upper bound for
+         * generating a private key in EC cryptosystems and should fit all valid
+         * secret scalars.
+         *
+         * For padding on export we just use the bit length of the order
+         * converted to bytes (rounding up).
+         *
+         * For preallocating the BIGNUM storage we look at the number of "words"
+         * required for the internal representation of the order, and we
+         * preallocate 2 extra "words" in case any of the subsequent processing
+         * might temporarily overflow the order length.
+         */
+        ecbits = EC_GROUP_order_bits(ecg);
+        if (ecbits <= 0)
+            goto err;
+        sz = (ecbits + 7 ) / 8;
+        if (!ossl_param_bld_push_BN_pad(tmpl,
+                                        OSSL_PKEY_PARAM_PRIV_KEY,
+                                        priv_key, sz))
+            goto err;
+    }
+
+    ret = 1;
+
+ err:
+    OPENSSL_free(pub_key);
+    return ret;
+}
+
+static ossl_inline
+int ec_set_param_ecdh_cofactor_mode(EC_KEY *ec, const OSSL_PARAM *p)
+{
+    const EC_GROUP *ecg = EC_KEY_get0_group(ec);
+    const BIGNUM *cofactor;
+    int mode;
+
+    if (!OSSL_PARAM_get_int(p, &mode))
+        return 0;
+
+    /*
+     * mode can be only 0 for disable, or 1 for enable here.
+     *
+     * This is in contrast with the same parameter on an ECDH EVP_PKEY_CTX that
+     * also supports mode == -1 with the meaning of "reset to the default for
+     * the associated key".
+     */
+    if (mode < 0 || mode > 1)
+        return 0;
+
+    if ((cofactor = EC_GROUP_get0_cofactor(ecg)) == NULL )
+        return 0;
+
+    /* ECDH cofactor mode has no effect if cofactor is 1 */
+    if (BN_is_one(cofactor))
+        return 1;
+
+    if (mode == 1)
+        EC_KEY_set_flags(ec, EC_FLAG_COFACTOR_ECDH);
+    else if (mode == 0)
+        EC_KEY_clear_flags(ec, EC_FLAG_COFACTOR_ECDH);
+
+    return 1;
+}
+
+static ossl_inline
+int params_to_otherparams(EC_KEY *ec, const OSSL_PARAM params[])
+{
+    const OSSL_PARAM *p;
+
+    if (ec == NULL)
+        return 0;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH);
+    if (p != NULL && !ec_set_param_ecdh_cofactor_mode(ec, p))
+        return 0;
+
+    return 1;
+}
+
+static ossl_inline
+int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl)
+{
+    int ecdh_cofactor_mode = 0;
+
+    if (ec == NULL)
+        return 0;
+
+    ecdh_cofactor_mode =
+        (EC_KEY_get_flags(ec) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
+    if (!ossl_param_bld_push_int(tmpl,
+                OSSL_PKEY_PARAM_USE_COFACTOR_ECDH,
+                ecdh_cofactor_mode))
+        return 0;
+
+    return 1;
+}
+
+static
+void *ec_newdata(void *provctx)
+{
+    return EC_KEY_new();
+}
+
+static
+void ec_freedata(void *keydata)
+{
+    EC_KEY_free(keydata);
+}
+
+static
+int ec_has(void *keydata, int selection)
+{
+    EC_KEY *ec = keydata;
+    int ok = 0;
+
+    if ((selection & EC_POSSIBLE_SELECTIONS) != 0)
+        ok = 1;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+        ok = ok && (EC_KEY_get0_public_key(ec) != NULL);
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        ok = ok && (EC_KEY_get0_private_key(ec) != NULL);
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+        ok = ok && (EC_KEY_get0_group(ec) != NULL);
+    /*
+     * We consider OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS to always be available,
+     * so no extra check is needed other than the previous one against
+     * EC_POSSIBLE_SELECTIONS.
+     */
+
+    return ok;
+}
+
+static
+int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+    EC_KEY *ec = keydata;
+    int ok = 0;
+
+    if (ec == NULL)
+        return 0;
+
+    /*
+     * In this implementation, we can export/import only keydata in the
+     * following combinations:
+     *   - domain parameters only
+     *   - public key with associated domain parameters (+optional other params)
+     *   - private key with associated public key and domain parameters
+     *         (+optional other params)
+     *
+     * This means:
+     *   - domain parameters must always be requested
+     *   - private key must be requested alongside public key
+     *   - other parameters must be requested only alongside a key
+     */
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
+        return 0;
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
+            && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0)
+        return 0;
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0
+            && (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return 0;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+        ok = ok && params_to_domparams(ec, params);
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        int include_private =
+            selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+        ok = ok && params_to_key(ec, params, include_private);
+    }
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+        ok = ok && params_to_otherparams(ec, params);
+
+    return ok;
+}
+
+static
+int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
+              void *cbarg)
+{
+    EC_KEY *ec = keydata;
+    OSSL_PARAM_BLD tmpl;
+    OSSL_PARAM *params = NULL;
+    int ok = 1;
+
+    if (ec == NULL)
+        return 0;
+
+    /*
+     * In this implementation, we can export/import only keydata in the
+     * following combinations:
+     *   - domain parameters only
+     *   - public key with associated domain parameters (+optional other params)
+     *   - private key with associated public key and domain parameters
+     *         (+optional other params)
+     *
+     * This means:
+     *   - domain parameters must always be requested
+     *   - private key must be requested alongside public key
+     *   - other parameters must be requested only alongside a key
+     */
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
+        return 0;
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
+            && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0)
+        return 0;
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0
+            && (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+        return 0;
+
+    ossl_param_bld_init(&tmpl);
+
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+        ok = ok && domparams_to_params(ec, &tmpl);
+    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+        int include_private =
+            selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+        ok = ok && key_to_params(ec, &tmpl, include_private);
+    }
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+        ok = ok && otherparams_to_params(ec, &tmpl);
+
+    if (!ok
+        || (params = ossl_param_bld_to_param(&tmpl)) == NULL)
+        return 0;
+
+    ok = param_cb(params, cbarg);
+    ossl_param_bld_free(params);
+    return ok;
+}
+
+/* IMEXPORT = IMPORT + EXPORT */
+
+# define EC_IMEXPORTABLE_DOM_PARAMETERS                          \
+    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_NAME, NULL, 0)
+# define EC_IMEXPORTABLE_PUBLIC_KEY                              \
+    OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0)
+# define EC_IMEXPORTABLE_PRIVATE_KEY                             \
+    OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+# define EC_IMEXPORTABLE_OTHER_PARAMETERS                        \
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL)
+
+/*
+ * Include all the possible combinations of OSSL_PARAM arrays for
+ * ec_imexport_types().
+ *
+ * They are in a separate file as it is ~100 lines of unreadable and
+ * uninteresting machine generated stuff.
+ *
+ * TODO(3.0): the generated list looks quite ugly, as to cover all possible
+ * combinations of the bits in `selection`, it also includes combinations that
+ * are not really useful: we might want to consider alternatives to this
+ * solution.
+ */
+#include "ec_kmgmt_imexport.inc"
+
+static ossl_inline
+const OSSL_PARAM *ec_imexport_types(int selection)
+{
+    int type_select = 0;
+
+    if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+        type_select += 1;
+    if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+        type_select += 2;
+    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+        type_select += 4;
+    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+        type_select += 8;
+    return ec_types[type_select];
+}
+
+static
+const OSSL_PARAM *ec_import_types(int selection)
+{
+    return ec_imexport_types(selection);
+}
+
+static
+const OSSL_PARAM *ec_export_types(int selection)
+{
+    return ec_imexport_types(selection);
+}
+
+static
+int ec_get_params(void *key, OSSL_PARAM params[])
+{
+    EC_KEY *eck = key;
+    const EC_GROUP *ecg = NULL;
+    OSSL_PARAM *p;
+
+    ecg = EC_KEY_get0_group(eck);
+    if (ecg == NULL)
+        return 0;
+
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+        && !OSSL_PARAM_set_int(p, ECDSA_size(eck)))
+        return 0;
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+        && !OSSL_PARAM_set_int(p, EC_GROUP_order_bits(ecg)))
+        return 0;
+    if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL) {
+        int ecbits, sec_bits;
+
+        ecbits = EC_GROUP_order_bits(ecg);
+
+        /*
+         * The following estimates are based on the values published
+         * in Table 2 of "NIST Special Publication 800-57 Part 1 Revision 4"
+         * at http://dx.doi.org/10.6028/NIST.SP.800-57pt1r4 .
+         *
+         * Note that the above reference explicitly categorizes algorithms in a
+         * discrete set of values {80, 112, 128, 192, 256}, and that it is
+         * relevant only for NIST approved Elliptic Curves, while OpenSSL
+         * applies the same logic also to other curves.
+         *
+         * Classifications produced by other standardazing bodies might differ,
+         * so the results provided for "bits of security" by this provider are
+         * to be considered merely indicative, and it is the users'
+         * responsibility to compare these values against the normative
+         * references that may be relevant for their intent and purposes.
+         */
+        if (ecbits >= 512)
+            sec_bits = 256;
+        else if (ecbits >= 384)
+            sec_bits = 192;
+        else if (ecbits >= 256)
+            sec_bits = 128;
+        else if (ecbits >= 224)
+            sec_bits = 112;
+        else if (ecbits >= 160)
+            sec_bits = 80;
+        else
+            sec_bits = ecbits / 2;
+
+        if (!OSSL_PARAM_set_int(p, sec_bits))
+            return 0;
+    }
+
+    p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH);
+    if (p != NULL) {
+        int ecdh_cofactor_mode = 0;
+
+        ecdh_cofactor_mode =
+            (EC_KEY_get_flags(eck) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
+
+        if (!OSSL_PARAM_set_int(p, ecdh_cofactor_mode))
+            return 0;
+    }
+
+    return 1;
+}
+
+static const OSSL_PARAM ec_known_gettable_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ec_gettable_params(void)
+{
+    return ec_known_gettable_params;
+}
+
+static const OSSL_PARAM ec_known_settable_params[] = {
+    OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+    OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ec_settable_params(void)
+{
+    return ec_known_settable_params;
+}
+
+static
+int ec_set_params(void *key, const OSSL_PARAM params[])
+{
+    EC_KEY *eck = key;
+    const OSSL_PARAM *p;
+
+    p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH);
+    if (p != NULL && !ec_set_param_ecdh_cofactor_mode(eck, p))
+        return 0;
+
+    return 1;
+}
+
+const OSSL_DISPATCH ec_keymgmt_functions[] = {
+    { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ec_newdata },
+    { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ec_freedata },
+    { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))ec_get_params },
+    { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))ec_gettable_params },
+    { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))ec_set_params },
+    { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))ec_settable_params },
+    { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ec_has },
+    { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ec_import },
+    { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ec_import_types },
+    { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ec_export },
+    { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ec_export_types },
+    { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+        (void (*)(void))ec_query_operation_name },
+    { 0, NULL }
+};
diff --git a/providers/implementations/keymgmt/ec_kmgmt_imexport.inc b/providers/implementations/keymgmt/ec_kmgmt_imexport.inc
new file mode 100644
index 0000000000..58eb09a3e4
--- /dev/null
+++ b/providers/implementations/keymgmt/ec_kmgmt_imexport.inc
@@ -0,0 +1,100 @@
+/*
+ * This file is meant to be included from ec_kmgmt.c
+ */
+
+static const OSSL_PARAM ec_private_key_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_public_key_types[] = {
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_key_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_dom_parameters_types[] = {
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_5_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_6_types[] = {
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_key_domp_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_other_parameters_types[] = {
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_9_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_10_types[] = {
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_11_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_all_parameters_types[] = {
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_13_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_14_types[] = {
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_all_types[] = {
+    EC_IMEXPORTABLE_PRIVATE_KEY,
+    EC_IMEXPORTABLE_PUBLIC_KEY,
+    EC_IMEXPORTABLE_DOM_PARAMETERS,
+    EC_IMEXPORTABLE_OTHER_PARAMETERS,
+    OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ec_types[] = {
+    NULL,
+    ec_private_key_types,
+    ec_public_key_types,
+    ec_key_types,
+    ec_dom_parameters_types,
+    ec_5_types,
+    ec_6_types,
+    ec_key_domp_types,
+    ec_other_parameters_types,
+    ec_9_types,
+    ec_10_types,
+    ec_11_types,
+    ec_all_parameters_types,
+    ec_13_types,
+    ec_14_types,
+    ec_all_types
+};
diff --git a/test/recipes/80-test_cms.t b/test/recipes/80-test_cms.t
index ee227f3cdb..fd66557616 100644
--- a/test/recipes/80-test_cms.t
+++ b/test/recipes/80-test_cms.t
@@ -27,7 +27,7 @@ my $smcont   = srctop_file("test", "smcont.txt");
 my ($no_des, $no_dh, $no_dsa, $no_ec, $no_ec2m, $no_rc2, $no_zlib)
     = disabled qw/des dh dsa ec ec2m rc2 zlib/;
 
-plan tests => 6;
+plan tests => 7;
 
 my @smime_pkcs7_tests = (
 
@@ -631,6 +631,24 @@ subtest "CMS Check that bad attributes fail when verifying signers\n" => sub {
     }
 };
 
+subtest "CMS Decrypt message encrypted with OpenSSL 1.1.1\n" => sub {
+    plan tests => 1;
+
+    SKIP: {
+        skip "EC isn't supported in this build", 1
+            if disabled("ec");
+
+        my $out = "smtst.txt";
+
+        ok(run(app(["openssl", "cms", "-decrypt",
+                    "-inkey", catfile($smdir, "smec3.pem"),
+                    "-in", catfile($datadir, "ciphertext_from_1_1_1.cms"),
+                    "-out", $out ]))
+           && compare_text($smcont, $out) == 0,
+           "Decrypt message from OpenSSL 1.1.1");
+    }
+};
+
 sub check_availability {
     my $tnam = shift;
 
diff --git a/test/recipes/80-test_cms_data/ciphertext_from_1_1_1.cms b/test/recipes/80-test_cms_data/ciphertext_from_1_1_1.cms
new file mode 100644
index 0000000000..1f291931ec
--- /dev/null
+++ b/test/recipes/80-test_cms_data/ciphertext_from_1_1_1.cms
@@ -0,0 +1,20 @@
+MIME-Version: 1.0
+Content-Disposition: attachment; filename="smime.p7m"
+Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
+Content-Transfer-Encoding: base64
+
+MIAGCSqGSIb3DQEHA6CAMIACAQIxggHwoYH1AgEDoFGhTzAJBgcqhkjOPQIBA0IA
+BAePxHUnwKL0d8UFDKE1Ey90FGDkwsy1iTttSmKeUB2ZJoM1TwbPUI9YVsSttJNV
+x+25aQ1Qnw3FnY03rcUcy94wHAYJK4EFEIZIPwACMA8GCyqGSIb3DQEJEAMGBQAw
+fzB9MFEwRDELMAkGA1UEBhMCVUsxFjAUBgNVBAoMDU9wZW5TU0wgR3JvdXAxHTAb
+BgNVBAMMFFRlc3QgUy9NSU1FIFJTQSBSb290AgkA9oQ6WVaz+mMEKFSg1EdBE4qA
+rh2DJVuTJfopaDXM4ih25kIGcxz+zRTo0+8Z7XgjE1KhgfUCAQOgUaFPMAkGByqG
+SM49AgEDQgAErpcZ+4D3r+tERXL9c8pvtyRmNlYeMa7iCaVJ+YdLFtohdfrQ017/
+8CR+Q/q5ibL+eqDeg6KOxytDs2GpD+WoUTAcBgkrgQUQhkg/AAIwDwYLKoZIhvcN
+AQkQAwYFADB/MH0wUTBEMQswCQYDVQQGEwJVSzEWMBQGA1UECgwNT3BlblNTTCBH
+cm91cDEdMBsGA1UEAwwUVGVzdCBTL01JTUUgUlNBIFJvb3QCCQDZOZbupksgRgQo
+6OoZPATNvHJTypifw4rBfVlmDvUYcE6S7VmeEFDW9ALPw1XDa58lnTCABgkqhkiG
+9w0BBwEwFAYIKoZIhvcNAwcECKGDGhF9W/7SoIAEUJW5dEUWWwA6vIO1Db9aLJry
+Wx1zNmkFzJnkCUdsgrZxEIdT4kG5E+gLtGmA0+OO8RcoULhAWLf+s2yNN3vLTshN
+YJpevytIYpXQQgzJ8x+5BAjSDDgrrWnBugAAAAAAAAAAAAA=
+
diff --git a/util/libcrypto.num b/util/libcrypto.num
index 9029688674..50f1f1f745 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -4935,3 +4935,13 @@ X509_STORE_get1_all_certs               ?	3_0_0	EXIST::FUNCTION:
 OSSL_CMP_validate_msg                   ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_validate_cert_path             ?	3_0_0	EXIST::FUNCTION:CMP
 OSSL_CMP_print_to_bio                   ?	3_0_0	EXIST::FUNCTION:CMP
+EVP_PKEY_CTX_set_ecdh_cofactor_mode     ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_get_ecdh_cofactor_mode     ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_set_ecdh_kdf_type          ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_get_ecdh_kdf_type          ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_set_ecdh_kdf_md            ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_get_ecdh_kdf_md            ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_set_ecdh_kdf_outlen        ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_get_ecdh_kdf_outlen        ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_set0_ecdh_kdf_ukm          ?	3_0_0	EXIST::FUNCTION:EC
+EVP_PKEY_CTX_get0_ecdh_kdf_ukm          ?	3_0_0	EXIST::FUNCTION:EC


More information about the openssl-commits mailing list