+struct ec_gen_ctx {
+ OPENSSL_CTX *libctx;
+
+ EC_GROUP *gen_group;
+ int selection;
+};
+
+static void *ec_gen_init(void *provctx, int selection)
+{
+ OPENSSL_CTX *libctx = PROV_LIBRARY_CONTEXT_OF(provctx);
+ struct ec_gen_ctx *gctx = NULL;
+
+ if ((selection & (EC_POSSIBLE_SELECTIONS)) == 0)
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->libctx = libctx;
+ gctx->gen_group = NULL;
+ gctx->selection = selection;
+ }
+ return gctx;
+}
+
+static int ec_gen_set_group(void *genctx, int nid)
+{
+ struct ec_gen_ctx *gctx = genctx;
+ EC_GROUP *group;
+
+ group = EC_GROUP_new_by_curve_name_ex(gctx->libctx, nid);
+ if (group == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
+ return 0;
+ }
+ EC_GROUP_free(gctx->gen_group);
+ gctx->gen_group = group;
+ return 1;
+}
+static int ec_gen_set_template(void *genctx, void *templ)
+{
+ struct ec_gen_ctx *gctx = genctx;
+ EC_KEY *ec = templ;
+ const EC_GROUP *ec_group;
+
+ if (gctx == NULL || ec == NULL)
+ return 0;
+ if ((ec_group = EC_KEY_get0_group(ec)) == NULL)
+ return 0;
+ return ec_gen_set_group(gctx, EC_GROUP_get_curve_name(ec_group));
+}
+
+static int ec_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct ec_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_EC_NAME))
+ != NULL) {
+ const char *curve_name = NULL;
+ int ret = 0;
+
+ switch (p->data_type) {
+ case OSSL_PARAM_UTF8_STRING:
+ /* The OSSL_PARAM functions have no support for this */
+ curve_name = p->data;
+ ret = (curve_name != NULL);
+ break;
+ case OSSL_PARAM_UTF8_PTR:
+ ret = OSSL_PARAM_get_utf8_ptr(p, &curve_name);
+ break;
+ }
+
+ if (ret) {
+ int nid = ec_curve_name2nid(curve_name);
+
+ if (nid == NID_undef) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
+ ret = 0;
+ } else {
+ ret = ec_gen_set_group(gctx, nid);
+ }
+ }
+ return ret;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *ec_gen_settable_params(void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ { OSSL_PKEY_PARAM_EC_NAME, OSSL_PARAM_UTF8_STRING, NULL, 0, 0 },
+ OSSL_PARAM_END
+ };
+
+ return settable;
+}
+
+static int ec_gen_get_params(void *genctx, OSSL_PARAM params[])
+{
+ struct ec_gen_ctx *gctx = genctx;
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_NAME)) != NULL) {
+ int nid = EC_GROUP_get_curve_name(gctx->gen_group);
+ int ret = 0;
+ const char *curve_name = ec_curve_nid2name(nid);
+
+ switch (p->data_type) {
+ case OSSL_PARAM_UTF8_STRING:
+ ret = OSSL_PARAM_set_utf8_string(p, curve_name);
+ break;
+ case OSSL_PARAM_UTF8_PTR:
+ ret = OSSL_PARAM_set_utf8_ptr(p, curve_name);
+ break;
+ }
+ return ret;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *ec_gen_gettable_params(void *provctx)
+{
+ static OSSL_PARAM gettable[] = {
+ { OSSL_PKEY_PARAM_EC_NAME, OSSL_PARAM_UTF8_PTR, NULL, 0, 0 },
+ OSSL_PARAM_END
+ };
+
+ return gettable;
+}
+
+static int ec_gen_assign_group(EC_KEY *ec, EC_GROUP *group)
+{
+ if (group == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET);
+ return 0;
+ }
+ return EC_KEY_set_group(ec, group) > 0;
+}
+
+/*
+ * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation
+ */
+static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct ec_gen_ctx *gctx = genctx;
+ EC_KEY *ec = NULL;
+ int ret = 1; /* Start optimistically */
+
+ if (gctx == NULL
+ || (ec = EC_KEY_new_ex(gctx->libctx)) == NULL)
+ return NULL;
+
+ /* We must always assign a group, no matter what */
+ ret = ec_gen_assign_group(ec, gctx->gen_group);
+ /* Whether you want it or not, you get a keypair, not just one half */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ ret = ret && EC_KEY_generate_key(ec);
+
+ if (ret)
+ return ec;
+
+ /* Something went wrong, throw the key away */
+ EC_KEY_free(ec);
+ return NULL;
+}
+
+static void ec_gen_cleanup(void *genctx)
+{
+ struct ec_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ EC_GROUP_free(gctx->gen_group);
+ OPENSSL_free(gctx);
+}
+