<commit_before>/*
 * Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * 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 <string.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/proverr.h>
#include "crypto/bn.h"
#include "crypto/ec.h"
#include "prov/implementations.h"
#include "prov/providercommon.h"
#include "prov/provider_ctx.h"
#include "internal/param_build_set.h"

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
#  include "crypto/sm2.h"
# endif
#endif

static OSSL_FUNC_keymgmt_new_fn ec_newdata;
static OSSL_FUNC_keymgmt_gen_init_fn ec_gen_init;
static OSSL_FUNC_keymgmt_gen_set_template_fn ec_gen_set_template;
static OSSL_FUNC_keymgmt_gen_set_params_fn ec_gen_set_params;
static OSSL_FUNC_keymgmt_gen_settable_params_fn ec_gen_settable_params;
static OSSL_FUNC_keymgmt_gen_fn ec_gen;
static OSSL_FUNC_keymgmt_gen_cleanup_fn ec_gen_cleanup;
static OSSL_FUNC_keymgmt_load_fn ec_load;
static OSSL_FUNC_keymgmt_free_fn ec_freedata;
static OSSL_FUNC_keymgmt_get_params_fn ec_get_params;
static OSSL_FUNC_keymgmt_gettable_params_fn ec_gettable_params;
static OSSL_FUNC_keymgmt_set_params_fn ec_set_params;
static OSSL_FUNC_keymgmt_settable_params_fn ec_settable_params;
static OSSL_FUNC_keymgmt_has_fn ec_has;
static OSSL_FUNC_keymgmt_match_fn ec_match;
static OSSL_FUNC_keymgmt_validate_fn ec_validate;
static OSSL_FUNC_keymgmt_import_fn ec_import;
static OSSL_FUNC_keymgmt_import_types_fn ec_import_types;
static OSSL_FUNC_keymgmt_export_fn ec_export;
static OSSL_FUNC_keymgmt_export_types_fn ec_export_types;
static OSSL_FUNC_keymgmt_query_operation_name_fn ec_query_operation_name;
static OSSL_FUNC_keymgmt_dup_fn ec_dup;
#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static OSSL_FUNC_keymgmt_new_fn sm2_newdata;
static OSSL_FUNC_keymgmt_gen_init_fn sm2_gen_init;
static OSSL_FUNC_keymgmt_gen_fn sm2_gen;
static OSSL_FUNC_keymgmt_get_params_fn sm2_get_params;
static OSSL_FUNC_keymgmt_gettable_params_fn sm2_gettable_params;
static OSSL_FUNC_keymgmt_settable_params_fn sm2_settable_params;
static OSSL_FUNC_keymgmt_import_fn sm2_import;
static OSSL_FUNC_keymgmt_query_operation_name_fn sm2_query_operation_name;
static OSSL_FUNC_keymgmt_validate_fn sm2_validate;
# endif
#endif

#define EC_DEFAULT_MD "SHA256"
#define EC_POSSIBLE_SELECTIONS                                                 \
    (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS)
#define SM2_DEFAULT_MD "SM3"

static
const char *ec_query_operation_name(int operation_id)
{
    switch (operation_id) {
    case OSSL_OP_KEYEXCH:
        return "ECDH";
    case OSSL_OP_SIGNATURE:
        return "ECDSA";
    }
    return NULL;
}

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static
const char *sm2_query_operation_name(int operation_id)
{
    switch (operation_id) {
    case OSSL_OP_SIGNATURE:
        return "SM2";
    }
    return NULL;
}
# endif
#endif

/*
 * 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,
                  OSSL_PARAM params[], int include_private,
                  unsigned char **pub_key)
{
    BIGNUM *x = NULL, *y = NULL;
    const BIGNUM *priv_key = NULL;
    const EC_POINT *pub_point = NULL;
    const EC_GROUP *ecg = NULL;
    size_t pub_key_len = 0;
    int ret = 0;
    BN_CTX *bnctx = NULL;

    if (eckey == NULL
        || (ecg = EC_KEY_get0_group(eckey)) == NULL)
        return 0;

    priv_key = EC_KEY_get0_private_key(eckey);
    pub_point = EC_KEY_get0_public_key(eckey);

    if (pub_point != NULL) {
        OSSL_PARAM *p = NULL, *px = NULL, *py = NULL;
        /*
         * EC_POINT_point2buf() can generate random numbers in some
         * implementations so we need to ensure we use the correct libctx.
         */
        bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eckey));
        if (bnctx == NULL)
            goto err;


        /* If we are doing a get then check first before decoding the point */
        if (tmpl == NULL) {
            p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
            px = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_X);
            py = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_Y);
        }

        if (p != NULL || tmpl != NULL) {
            /* convert pub_point to a octet string according to the SECG standard */
            if ((pub_key_len = EC_POINT_point2buf(ecg, pub_point,
                                                  POINT_CONVERSION_COMPRESSED,
                                                  pub_key, bnctx)) == 0
                || !ossl_param_build_set_octet_string(tmpl, p,
                                                      OSSL_PKEY_PARAM_PUB_KEY,
                                                      *pub_key, pub_key_len))
                goto err;
        }
        if (px != NULL || py != NULL) {
            if (px != NULL)
                x = BN_CTX_get(bnctx);
            if (py != NULL)
                y = BN_CTX_get(bnctx);

            if (!EC_POINT_get_affine_coordinates(ecg, pub_point, x, y, bnctx))
                goto err;
            if (px != NULL
                && !ossl_param_build_set_bn(tmpl, px,
                                            OSSL_PKEY_PARAM_EC_PUB_X, x))
                goto err;
            if (py != NULL
                && !ossl_param_build_set_bn(tmpl, py,
                                            OSSL_PKEY_PARAM_EC_PUB_Y, y))
                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_build_set_bn_pad(tmpl, params,
                                         OSSL_PKEY_PARAM_PRIV_KEY,
                                         priv_key, sz))
            goto err;
    }
    ret = 1;
 err:
    BN_CTX_free(bnctx);
    return ret;
}

static ossl_inline
int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl,
                          OSSL_PARAM params[])
{
    int ecdh_cofactor_mode = 0, group_check = 0;
    const char *name = NULL;
    point_conversion_form_t format;

    if (ec == NULL)
        return 0;

    format = EC_KEY_get_conv_form(ec);
    name = ossl_ec_pt_format_id2name((int)format);
    if (name != NULL
        && !ossl_param_build_set_utf8_string(tmpl, params,
                                             OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
                                             name))
        return 0;

    group_check = EC_KEY_get_flags(ec) & EC_FLAG_CHECK_NAMED_GROUP_MASK;
    name = ossl_ec_check_group_type_id2name(group_check);
    if (name != NULL
        && !ossl_param_build_set_utf8_string(tmpl, params,
                                             OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE,
                                             name))
        return 0;

    if ((EC_KEY_get_enc_flags(ec) & EC_PKEY_NO_PUBKEY) != 0
            && !ossl_param_build_set_int(tmpl, params,
                                         OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 0))
        return 0;

    ecdh_cofactor_mode =
        (EC_KEY_get_flags(ec) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
    return ossl_param_build_set_int(tmpl, params,
                                    OSSL_PKEY_PARAM_USE_COFACTOR_ECDH,
                                    ecdh_cofactor_mode);
}

static
void *ec_newdata(void *provctx)
{
    if (!ossl_prov_is_running())
        return NULL;
    return EC_KEY_new_ex(PROV_LIBCTX_OF(provctx), NULL);
}

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static
void *sm2_newdata(void *provctx)
{
    if (!ossl_prov_is_running())
        return NULL;
    return EC_KEY_new_by_curve_name_ex(PROV_LIBCTX_OF(provctx), NULL, NID_sm2);
}
# endif
#endif

static
void ec_freedata(void *keydata)
{
    EC_KEY_free(keydata);
}

static
int ec_has(const void *keydata, int selection)
{
    const EC_KEY *ec = keydata;
    int ok = 1;

    if (!ossl_prov_is_running() || ec == NULL)
        return 0;
    if ((selection & EC_POSSIBLE_SELECTIONS) == 0)
        return 1; /* the selection is not missing */

    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_match(const void *keydata1, const void *keydata2, int selection)
{
    const EC_KEY *ec1 = keydata1;
    const EC_KEY *ec2 = keydata2;
    const EC_GROUP *group_a = EC_KEY_get0_group(ec1);
    const EC_GROUP *group_b = EC_KEY_get0_group(ec2);
    BN_CTX *ctx = NULL;
    int ok = 1;

    if (!ossl_prov_is_running())
        return 0;

    ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec1));
    if (ctx == NULL)
        return 0;

    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
        ok = ok && group_a != NULL && group_b != NULL
            && EC_GROUP_cmp(group_a, group_b, ctx) == 0;
    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
        int key_checked = 0;

        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
            const EC_POINT *pa = EC_KEY_get0_public_key(ec1);
            const EC_POINT *pb = EC_KEY_get0_public_key(ec2);

            if (pa != NULL && pb != NULL) {
                ok = ok && EC_POINT_cmp(group_b, pa, pb, ctx) == 0;
                key_checked = 1;
            }
        }
        if (!key_checked
            && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
            const BIGNUM *pa = EC_KEY_get0_private_key(ec1);
            const BIGNUM *pb = EC_KEY_get0_private_key(ec2);

            if (pa != NULL && pb != NULL) {
                ok = ok && BN_cmp(pa, pb) == 0;
                key_checked = 1;
            }
        }
        ok = ok && key_checked;
    }
    BN_CTX_free(ctx);
    return ok;
}

static int common_check_sm2(const EC_KEY *ec, int sm2_wanted)
{
    const EC_GROUP *ecg = NULL;

    /*
     * sm2_wanted: import the keys or domparams only on SM2 Curve
     * !sm2_wanted: import the keys or domparams only not on SM2 Curve
     */
    if ((ecg = EC_KEY_get0_group(ec)) == NULL
        || (sm2_wanted ^ (EC_GROUP_get_curve_name(ecg) == NID_sm2)))
        return 0;
    return 1;
}

static
int common_import(void *keydata, int selection, const OSSL_PARAM params[],
                  int sm2_wanted)
{
    EC_KEY *ec = keydata;
    int ok = 1;

    if (!ossl_prov_is_running() || ec == NULL)
        return 0;

    /*
     * In this implementation, we can export/import only keydata in the
     * following combinations:
     *   - domain parameters (+optional other params)
     *   - public key with associated domain parameters (+optional other params)
     *   - private key with associated domain parameters and optional public key
     *         (+optional other params)
     *
     * This means:
     *   - domain parameters must always be requested
     *   - private key must be requested alongside public key
     *   - other parameters are always optional
     */
    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
        return 0;

    ok = ok && ossl_ec_group_fromdata(ec, params);

    if (!common_check_sm2(ec, sm2_wanted))
        return 0;

    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
        int include_private =
            selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;

        ok = ok && ossl_ec_key_fromdata(ec, params, include_private);
    }
    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
        ok = ok && ossl_ec_key_otherparams_fromdata(ec, params);

    return ok;
}

static
int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
{
    return common_import(keydata, selection, params, 0);
}

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static
int sm2_import(void *keydata, int selection, const OSSL_PARAM params[])
{
    return common_import(keydata, selection, params, 1);
}
# endif
#endif

static
int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
              void *cbarg)
{
    EC_KEY *ec = keydata;
    OSSL_PARAM_BLD *tmpl = NULL;
    OSSL_PARAM *params = NULL;
    unsigned char *pub_key = NULL, *genbuf = NULL;
    BN_CTX *bnctx = NULL;
    int ok = 1;

    if (!ossl_prov_is_running() || ec == NULL)
        return 0;

    /*
     * In this implementation, we can export/import only keydata in the
     * following combinations:
     *   - domain parameters (+optional other params)
     *   - 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 are always optional
     */
    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;

    tmpl = OSSL_PARAM_BLD_new();
    if (tmpl == NULL)
        return 0;

    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
        bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
        if (bnctx == NULL) {
            ok = 0;
            goto end;
        }
        BN_CTX_start(bnctx);
        ok = ok && ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL,
                                        ossl_ec_key_get_libctx(ec),
                                        ossl_ec_key_get0_propq(ec),
                                        bnctx, &genbuf);
    }

    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, NULL, include_private, &pub_key);
    }
    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
        ok = ok && otherparams_to_params(ec, tmpl, NULL);

    if (ok && (params = OSSL_PARAM_BLD_to_param(tmpl)) != NULL)
        ok = param_cb(params, cbarg);
end:
    OSSL_PARAM_free(params);
    OSSL_PARAM_BLD_free(tmpl);
    OPENSSL_free(pub_key);
    OPENSSL_free(genbuf);
    BN_CTX_end(bnctx);
    BN_CTX_free(bnctx);
    return ok;
}

/* IMEXPORT = IMPORT + EXPORT */

# define EC_IMEXPORTABLE_DOM_PARAMETERS                                        \
    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),               \
    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),              \
    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),\<commit_msg>Don't report success from ec_export if OSSL_PARAM_BLD_to_param failed<commit_after>/*
 * Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * 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 <string.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_names.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/proverr.h>
#include "crypto/bn.h"
#include "crypto/ec.h"
#include "prov/implementations.h"
#include "prov/providercommon.h"
#include "prov/provider_ctx.h"
#include "internal/param_build_set.h"

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
#  include "crypto/sm2.h"
# endif
#endif

static OSSL_FUNC_keymgmt_new_fn ec_newdata;
static OSSL_FUNC_keymgmt_gen_init_fn ec_gen_init;
static OSSL_FUNC_keymgmt_gen_set_template_fn ec_gen_set_template;
static OSSL_FUNC_keymgmt_gen_set_params_fn ec_gen_set_params;
static OSSL_FUNC_keymgmt_gen_settable_params_fn ec_gen_settable_params;
static OSSL_FUNC_keymgmt_gen_fn ec_gen;
static OSSL_FUNC_keymgmt_gen_cleanup_fn ec_gen_cleanup;
static OSSL_FUNC_keymgmt_load_fn ec_load;
static OSSL_FUNC_keymgmt_free_fn ec_freedata;
static OSSL_FUNC_keymgmt_get_params_fn ec_get_params;
static OSSL_FUNC_keymgmt_gettable_params_fn ec_gettable_params;
static OSSL_FUNC_keymgmt_set_params_fn ec_set_params;
static OSSL_FUNC_keymgmt_settable_params_fn ec_settable_params;
static OSSL_FUNC_keymgmt_has_fn ec_has;
static OSSL_FUNC_keymgmt_match_fn ec_match;
static OSSL_FUNC_keymgmt_validate_fn ec_validate;
static OSSL_FUNC_keymgmt_import_fn ec_import;
static OSSL_FUNC_keymgmt_import_types_fn ec_import_types;
static OSSL_FUNC_keymgmt_export_fn ec_export;
static OSSL_FUNC_keymgmt_export_types_fn ec_export_types;
static OSSL_FUNC_keymgmt_query_operation_name_fn ec_query_operation_name;
static OSSL_FUNC_keymgmt_dup_fn ec_dup;
#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static OSSL_FUNC_keymgmt_new_fn sm2_newdata;
static OSSL_FUNC_keymgmt_gen_init_fn sm2_gen_init;
static OSSL_FUNC_keymgmt_gen_fn sm2_gen;
static OSSL_FUNC_keymgmt_get_params_fn sm2_get_params;
static OSSL_FUNC_keymgmt_gettable_params_fn sm2_gettable_params;
static OSSL_FUNC_keymgmt_settable_params_fn sm2_settable_params;
static OSSL_FUNC_keymgmt_import_fn sm2_import;
static OSSL_FUNC_keymgmt_query_operation_name_fn sm2_query_operation_name;
static OSSL_FUNC_keymgmt_validate_fn sm2_validate;
# endif
#endif

#define EC_DEFAULT_MD "SHA256"
#define EC_POSSIBLE_SELECTIONS                                                 \
    (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS)
#define SM2_DEFAULT_MD "SM3"

static
const char *ec_query_operation_name(int operation_id)
{
    switch (operation_id) {
    case OSSL_OP_KEYEXCH:
        return "ECDH";
    case OSSL_OP_SIGNATURE:
        return "ECDSA";
    }
    return NULL;
}

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static
const char *sm2_query_operation_name(int operation_id)
{
    switch (operation_id) {
    case OSSL_OP_SIGNATURE:
        return "SM2";
    }
    return NULL;
}
# endif
#endif

/*
 * 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,
                  OSSL_PARAM params[], int include_private,
                  unsigned char **pub_key)
{
    BIGNUM *x = NULL, *y = NULL;
    const BIGNUM *priv_key = NULL;
    const EC_POINT *pub_point = NULL;
    const EC_GROUP *ecg = NULL;
    size_t pub_key_len = 0;
    int ret = 0;
    BN_CTX *bnctx = NULL;

    if (eckey == NULL
        || (ecg = EC_KEY_get0_group(eckey)) == NULL)
        return 0;

    priv_key = EC_KEY_get0_private_key(eckey);
    pub_point = EC_KEY_get0_public_key(eckey);

    if (pub_point != NULL) {
        OSSL_PARAM *p = NULL, *px = NULL, *py = NULL;
        /*
         * EC_POINT_point2buf() can generate random numbers in some
         * implementations so we need to ensure we use the correct libctx.
         */
        bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eckey));
        if (bnctx == NULL)
            goto err;


        /* If we are doing a get then check first before decoding the point */
        if (tmpl == NULL) {
            p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
            px = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_X);
            py = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_Y);
        }

        if (p != NULL || tmpl != NULL) {
            /* convert pub_point to a octet string according to the SECG standard */
            if ((pub_key_len = EC_POINT_point2buf(ecg, pub_point,
                                                  POINT_CONVERSION_COMPRESSED,
                                                  pub_key, bnctx)) == 0
                || !ossl_param_build_set_octet_string(tmpl, p,
                                                      OSSL_PKEY_PARAM_PUB_KEY,
                                                      *pub_key, pub_key_len))
                goto err;
        }
        if (px != NULL || py != NULL) {
            if (px != NULL)
                x = BN_CTX_get(bnctx);
            if (py != NULL)
                y = BN_CTX_get(bnctx);

            if (!EC_POINT_get_affine_coordinates(ecg, pub_point, x, y, bnctx))
                goto err;
            if (px != NULL
                && !ossl_param_build_set_bn(tmpl, px,
                                            OSSL_PKEY_PARAM_EC_PUB_X, x))
                goto err;
            if (py != NULL
                && !ossl_param_build_set_bn(tmpl, py,
                                            OSSL_PKEY_PARAM_EC_PUB_Y, y))
                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_build_set_bn_pad(tmpl, params,
                                         OSSL_PKEY_PARAM_PRIV_KEY,
                                         priv_key, sz))
            goto err;
    }
    ret = 1;
 err:
    BN_CTX_free(bnctx);
    return ret;
}

static ossl_inline
int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl,
                          OSSL_PARAM params[])
{
    int ecdh_cofactor_mode = 0, group_check = 0;
    const char *name = NULL;
    point_conversion_form_t format;

    if (ec == NULL)
        return 0;

    format = EC_KEY_get_conv_form(ec);
    name = ossl_ec_pt_format_id2name((int)format);
    if (name != NULL
        && !ossl_param_build_set_utf8_string(tmpl, params,
                                             OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
                                             name))
        return 0;

    group_check = EC_KEY_get_flags(ec) & EC_FLAG_CHECK_NAMED_GROUP_MASK;
    name = ossl_ec_check_group_type_id2name(group_check);
    if (name != NULL
        && !ossl_param_build_set_utf8_string(tmpl, params,
                                             OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE,
                                             name))
        return 0;

    if ((EC_KEY_get_enc_flags(ec) & EC_PKEY_NO_PUBKEY) != 0
            && !ossl_param_build_set_int(tmpl, params,
                                         OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 0))
        return 0;

    ecdh_cofactor_mode =
        (EC_KEY_get_flags(ec) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
    return ossl_param_build_set_int(tmpl, params,
                                    OSSL_PKEY_PARAM_USE_COFACTOR_ECDH,
                                    ecdh_cofactor_mode);
}

static
void *ec_newdata(void *provctx)
{
    if (!ossl_prov_is_running())
        return NULL;
    return EC_KEY_new_ex(PROV_LIBCTX_OF(provctx), NULL);
}

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static
void *sm2_newdata(void *provctx)
{
    if (!ossl_prov_is_running())
        return NULL;
    return EC_KEY_new_by_curve_name_ex(PROV_LIBCTX_OF(provctx), NULL, NID_sm2);
}
# endif
#endif

static
void ec_freedata(void *keydata)
{
    EC_KEY_free(keydata);
}

static
int ec_has(const void *keydata, int selection)
{
    const EC_KEY *ec = keydata;
    int ok = 1;

    if (!ossl_prov_is_running() || ec == NULL)
        return 0;
    if ((selection & EC_POSSIBLE_SELECTIONS) == 0)
        return 1; /* the selection is not missing */

    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_match(const void *keydata1, const void *keydata2, int selection)
{
    const EC_KEY *ec1 = keydata1;
    const EC_KEY *ec2 = keydata2;
    const EC_GROUP *group_a = EC_KEY_get0_group(ec1);
    const EC_GROUP *group_b = EC_KEY_get0_group(ec2);
    BN_CTX *ctx = NULL;
    int ok = 1;

    if (!ossl_prov_is_running())
        return 0;

    ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec1));
    if (ctx == NULL)
        return 0;

    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
        ok = ok && group_a != NULL && group_b != NULL
            && EC_GROUP_cmp(group_a, group_b, ctx) == 0;
    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
        int key_checked = 0;

        if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
            const EC_POINT *pa = EC_KEY_get0_public_key(ec1);
            const EC_POINT *pb = EC_KEY_get0_public_key(ec2);

            if (pa != NULL && pb != NULL) {
                ok = ok && EC_POINT_cmp(group_b, pa, pb, ctx) == 0;
                key_checked = 1;
            }
        }
        if (!key_checked
            && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
            const BIGNUM *pa = EC_KEY_get0_private_key(ec1);
            const BIGNUM *pb = EC_KEY_get0_private_key(ec2);

            if (pa != NULL && pb != NULL) {
                ok = ok && BN_cmp(pa, pb) == 0;
                key_checked = 1;
            }
        }
        ok = ok && key_checked;
    }
    BN_CTX_free(ctx);
    return ok;
}

static int common_check_sm2(const EC_KEY *ec, int sm2_wanted)
{
    const EC_GROUP *ecg = NULL;

    /*
     * sm2_wanted: import the keys or domparams only on SM2 Curve
     * !sm2_wanted: import the keys or domparams only not on SM2 Curve
     */
    if ((ecg = EC_KEY_get0_group(ec)) == NULL
        || (sm2_wanted ^ (EC_GROUP_get_curve_name(ecg) == NID_sm2)))
        return 0;
    return 1;
}

static
int common_import(void *keydata, int selection, const OSSL_PARAM params[],
                  int sm2_wanted)
{
    EC_KEY *ec = keydata;
    int ok = 1;

    if (!ossl_prov_is_running() || ec == NULL)
        return 0;

    /*
     * In this implementation, we can export/import only keydata in the
     * following combinations:
     *   - domain parameters (+optional other params)
     *   - public key with associated domain parameters (+optional other params)
     *   - private key with associated domain parameters and optional public key
     *         (+optional other params)
     *
     * This means:
     *   - domain parameters must always be requested
     *   - private key must be requested alongside public key
     *   - other parameters are always optional
     */
    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
        return 0;

    ok = ok && ossl_ec_group_fromdata(ec, params);

    if (!common_check_sm2(ec, sm2_wanted))
        return 0;

    if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
        int include_private =
            selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;

        ok = ok && ossl_ec_key_fromdata(ec, params, include_private);
    }
    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
        ok = ok && ossl_ec_key_otherparams_fromdata(ec, params);

    return ok;
}

static
int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
{
    return common_import(keydata, selection, params, 0);
}

#ifndef FIPS_MODULE
# ifndef OPENSSL_NO_SM2
static
int sm2_import(void *keydata, int selection, const OSSL_PARAM params[])
{
    return common_import(keydata, selection, params, 1);
}
# endif
#endif

static
int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
              void *cbarg)
{
    EC_KEY *ec = keydata;
    OSSL_PARAM_BLD *tmpl = NULL;
    OSSL_PARAM *params = NULL;
    unsigned char *pub_key = NULL, *genbuf = NULL;
    BN_CTX *bnctx = NULL;
    int ok = 1;

    if (!ossl_prov_is_running() || ec == NULL)
        return 0;

    /*
     * In this implementation, we can export/import only keydata in the
     * following combinations:
     *   - domain parameters (+optional other params)
     *   - 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 are always optional
     */
    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;

    tmpl = OSSL_PARAM_BLD_new();
    if (tmpl == NULL)
        return 0;

    if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
        bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
        if (bnctx == NULL) {
            ok = 0;
            goto end;
        }
        BN_CTX_start(bnctx);
        ok = ok && ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL,
                                        ossl_ec_key_get_libctx(ec),
                                        ossl_ec_key_get0_propq(ec),
                                        bnctx, &genbuf);
    }

    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, NULL, include_private, &pub_key);
    }
    if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
        ok = ok && otherparams_to_params(ec, tmpl, NULL);

    if (ok && (params = OSSL_PARAM_BLD_to_param(tmpl)) != NULL)
        ok = param_cb(params, cbarg);
    else
        ok = 0;
end:
    OSSL_PARAM_free(params);
    OSSL_PARAM_BLD_free(tmpl);
    OPENSSL_free(pub_key);
    OPENSSL_free(genbuf);
    BN_CTX_end(bnctx);
    BN_CTX_free(bnctx);
    return ok;
}

/* IMEXPORT = IMPORT + EXPORT */

# define EC_IMEXPORTABLE_DOM_PARAMETERS                                        \
    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),               \
    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),              \
    OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),\
{'fraction_non_alphanumeric': 0.0810219188340678, 'fraction_numerical': 0.008784815230853423, 'mean_word_length': 2.4376237623762376, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 17, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 68, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6429601', 'n_tokens_mistral': 11820, 'n_tokens_neox': 11417, 'n_words': 2813}