+ return keydata;
+}
+
+#ifndef FIPS_MODULE
+int evp_pkey_downgrade(EVP_PKEY *pk)
+{
+ EVP_KEYMGMT *keymgmt = pk->keymgmt;
+ void *keydata = pk->keydata;
+ int type = pk->type;
+ const char *keytype = NULL;
+
+ /* If this isn't a provider side key, we're done */
+ if (keymgmt == NULL)
+ return 1;
+
+ keytype = evp_first_name(EVP_KEYMGMT_provider(keymgmt), keymgmt->name_id);
+
+ /*
+ * If the type is EVP_PKEY_NONE, then we have a problem somewhere else
+ * in our code. If it's not one of the well known EVP_PKEY_xxx values,
+ * it should at least be EVP_PKEY_KEYMGMT at this point.
+ * TODO(3.0) remove this check when we're confident that the rest of the
+ * code treats this correctly.
+ */
+ if (!ossl_assert(type != EVP_PKEY_NONE)) {
+ ERR_raise_data(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR,
+ "keymgmt key type = %s but legacy type = EVP_PKEY_NONE",
+ keytype);
+ return 0;
+ }
+
+ /* Prefer the legacy key type name for error reporting */
+ if (type != EVP_PKEY_KEYMGMT)
+ keytype = OBJ_nid2sn(type);
+
+ /*
+ * To be able to downgrade, we steal the provider side "origin" keymgmt
+ * and keydata. We've already grabbed the pointers, so all we need to
+ * do is clear those pointers in |pk| and then call evp_pkey_free_it().
+ * That way, we can restore |pk| if we need to.
+ */
+ pk->keymgmt = NULL;
+ pk->keydata = NULL;
+ evp_pkey_free_it(pk);
+ if (EVP_PKEY_set_type(pk, type)) {
+ /* If the key is typed but empty, we're done */
+ if (keydata == NULL) {
+ /* We're dropping the EVP_KEYMGMT */
+ EVP_KEYMGMT_free(keymgmt);
+ return 1;
+ }
+
+ if (pk->ameth->import_from == NULL) {
+ ERR_raise_data(ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION,
+ "key type = %s", keytype);
+ } else {
+ /*
+ * We perform the export in the same libctx as the keymgmt that we
+ * are using.
+ */
+ OPENSSL_CTX *libctx = ossl_provider_library_context(keymgmt->prov);
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_from_pkey(libctx, pk, NULL);
+ if (pctx == NULL)
+ ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+
+ if (pctx != NULL
+ && evp_keymgmt_export(keymgmt, keydata,
+ OSSL_KEYMGMT_SELECT_ALL,
+ pk->ameth->import_from, pctx)) {
+ /*
+ * Save the provider side data in the operation cache, so they'll
+ * find it again. evp_pkey_free_it() cleared the cache, so it's
+ * safe to assume slot zero is free.
+ * Note that evp_keymgmt_util_cache_keydata() increments keymgmt's
+ * reference count.
+ */
+ evp_keymgmt_util_cache_keydata(pk, 0, keymgmt, keydata);
+ EVP_PKEY_CTX_free(pctx);
+
+ /* Synchronize the dirty count */
+ pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
+
+ /* evp_keymgmt_export() increased the refcount... */
+ EVP_KEYMGMT_free(keymgmt);
+ return 1;
+ }
+ EVP_PKEY_CTX_free(pctx);
+ }
+
+ ERR_raise_data(ERR_LIB_EVP, EVP_R_KEYMGMT_EXPORT_FAILURE,
+ "key type = %s", keytype);
+ }
+
+ /*
+ * Something went wrong. This could for example happen if the keymgmt
+ * turns out to be an HSM implementation that refuses to let go of some
+ * of the key data, typically the private bits. In this case, we restore
+ * the provider side internal "origin" and leave it at that.
+ */
+ if (!ossl_assert(EVP_PKEY_set_type_by_keymgmt(pk, keymgmt))) {
+ /* This should not be impossible */
+ ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ /* EVP_PKEY_set_type_by_keymgmt() increased the refcount... */
+ EVP_KEYMGMT_free(keymgmt);
+ pk->keydata = keydata;
+ evp_keymgmt_util_cache_keyinfo(pk);
+ return 0; /* No downgrade, but at least the key is restored */
+}
+#endif /* FIPS_MODULE */
+
+const OSSL_PARAM *EVP_PKEY_gettable_params(EVP_PKEY *pkey)
+{
+ if (pkey == NULL
+ || pkey->keymgmt == NULL
+ || pkey->keydata == NULL)
+ return 0;
+ return evp_keymgmt_gettable_params(pkey->keymgmt);
+}
+
+int EVP_PKEY_get_bn_param(EVP_PKEY *pkey, const char *key_name, BIGNUM **bn)
+{
+ int ret = 0;
+ OSSL_PARAM params[2];
+ unsigned char buffer[2048];
+ unsigned char *buf = NULL;
+ size_t buf_sz = 0;
+
+ if (pkey == NULL
+ || pkey->keymgmt == NULL
+ || pkey->keydata == NULL
+ || key_name == NULL
+ || bn == NULL)
+ return 0;
+
+ memset(buffer, 0, sizeof(buffer));
+ params[0] = OSSL_PARAM_construct_BN(key_name, buffer, sizeof(buffer));
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params)) {
+ if (!OSSL_PARAM_modified(params) || params[0].return_size == 0)
+ return 0;
+ buf_sz = params[0].return_size;
+ /*
+ * If it failed because the buffer was too small then allocate the
+ * required buffer size and retry.
+ */
+ buf = OPENSSL_zalloc(buf_sz);
+ if (buf == NULL)
+ return 0;
+ params[0].data = buf;
+ params[0].data_size = buf_sz;
+
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params))
+ goto err;
+ }
+ /* Fail if the param was not found */
+ if (!OSSL_PARAM_modified(params))
+ goto err;
+ ret = OSSL_PARAM_get_BN(params, bn);
+err:
+ OPENSSL_free(buf);
+ return ret;
+}
+
+int EVP_PKEY_get_octet_string_param(EVP_PKEY *pkey, const char *key_name,
+ unsigned char *buf, size_t max_buf_sz,
+ size_t *out_sz)
+{
+ OSSL_PARAM params[2];
+
+ if (pkey == NULL
+ || pkey->keymgmt == NULL
+ || pkey->keydata == NULL
+ || key_name == NULL)
+ return 0;
+
+ params[0] = OSSL_PARAM_construct_octet_string(key_name, buf, max_buf_sz);
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params)
+ || !OSSL_PARAM_modified(params))
+ return 0;
+ if (out_sz != NULL)
+ *out_sz = params[0].return_size;
+ return 1;
+}
+
+int EVP_PKEY_get_utf8_string_param(EVP_PKEY *pkey, const char *key_name,
+ char *str, size_t max_buf_sz,
+ size_t *out_sz)
+{
+ OSSL_PARAM params[2];
+
+ if (pkey == NULL
+ || pkey->keymgmt == NULL
+ || pkey->keydata == NULL
+ || key_name == NULL)
+ return 0;
+
+ params[0] = OSSL_PARAM_construct_utf8_string(key_name, str, max_buf_sz);
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params)
+ || !OSSL_PARAM_modified(params))
+ return 0;
+ if (out_sz != NULL)
+ *out_sz = params[0].return_size;
+ return 1;
+}
+
+int EVP_PKEY_get_int_param(EVP_PKEY *pkey, const char *key_name, int *out)
+{
+ OSSL_PARAM params[2];
+
+ if (pkey == NULL
+ || pkey->keymgmt == NULL
+ || pkey->keydata == NULL
+ || key_name == NULL)
+ return 0;
+
+ params[0] = OSSL_PARAM_construct_int(key_name, out);
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params)
+ || !OSSL_PARAM_modified(params))
+ return 0;
+ return 1;
+}
+
+int EVP_PKEY_get_size_t_param(EVP_PKEY *pkey, const char *key_name, size_t *out)
+{
+ OSSL_PARAM params[2];
+
+ if (pkey == NULL
+ || pkey->keymgmt == NULL
+ || pkey->keydata == NULL
+ || key_name == NULL)
+ return 0;
+
+ params[0] = OSSL_PARAM_construct_size_t(key_name, out);
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params)
+ || !OSSL_PARAM_modified(params))
+ return 0;
+ return 1;