+
+#ifndef FIPS_MODE
+int evp_pkey_downgrade(EVP_PKEY *pk)
+{
+ EVP_KEYMGMT *keymgmt = pk->keymgmt;
+ void *keydata = pk->keydata;
+ int type = pk->save_type;
+ const char *keytype = NULL;
+
+ /* If this isn't a provider side key, we're done */
+ if (keymgmt == NULL)
+ return 1;
+
+ /* Get the key type name for error reporting */
+ if (type != EVP_PKEY_NONE)
+ keytype = OBJ_nid2sn(type);
+ else
+ keytype =
+ evp_first_name(EVP_KEYMGMT_provider(keymgmt), keymgmt->name_id);
+
+ /*
+ * |save_type| was set when any of the EVP_PKEY_set_type functions
+ * was called. It was set to EVP_PKEY_NONE if the key type wasn't
+ * recognised to be any of the legacy key types, and the downgrade
+ * isn't possible.
+ */
+ if (type == EVP_PKEY_NONE) {
+ ERR_raise_data(ERR_LIB_EVP, EVP_R_UNKNOWN_KEY_TYPE,
+ "key type = %s, can't downgrade", keytype);
+ return 0;
+ }
+
+ /*
+ * 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)
+ return 1;
+
+ if (pk->ameth->import_from == NULL) {
+ ERR_raise_data(ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION,
+ "key type = %s", keytype);
+ } else if (evp_keymgmt_export(keymgmt, keydata,
+ OSSL_KEYMGMT_SELECT_ALL,
+ pk->ameth->import_from, pk)) {
+ /*
+ * 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);
+
+ /* Synchronize the dirty count */
+ pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
+ return 1;
+ }
+
+ 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;
+ }
+ pk->keydata = keydata;
+ evp_keymgmt_util_cache_keyinfo(pk);
+ return 0; /* No downgrade, but at least the key is restored */
+}
+#endif /* FIPS_MODE */
+
+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);
+}
+
+/*
+ * For the following methods param->return_size is set to a value
+ * larger than can be returned by the call to evp_keymgmt_get_params().
+ * If it is still this value then the parameter was ignored - and in this
+ * case it returns an error..
+ */
+
+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];
+ /*
+ * Use -1 as the terminator here instead of sizeof(buffer) + 1 since
+ * -1 is less likely to be a valid value.
+ */
+ const size_t not_set = (size_t)-1;
+ 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));
+ /* If the return_size is still not_set then we know it was not found */
+ params[0].return_size = not_set;
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params)) {
+ if (params[0].return_size == not_set
+ || 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 (params[0].return_size == not_set)
+ 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];
+ const size_t not_set = max_buf_sz + 1;
+
+ 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[0].return_size = not_set;
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params))
+ return 0;
+ if (params[0].return_size == not_set)
+ 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];
+ const size_t not_set = max_buf_sz + 1;
+
+ 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[0].return_size = not_set;
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params))
+ return 0;
+ if (params[0].return_size == not_set)
+ 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];
+ const size_t not_set = sizeof(int) + 1;
+
+ if (pkey == NULL
+ || pkey->keymgmt == NULL
+ || pkey->keydata == NULL
+ || key_name == NULL)
+ return 0;
+
+ params[0] = OSSL_PARAM_construct_int(key_name, out);
+ params[0].return_size = not_set;
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params))
+ return 0;
+ if (params[0].return_size == not_set)
+ 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];
+ const size_t not_set = sizeof(size_t) + 1;
+
+ 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[0].return_size = not_set;
+ params[1] = OSSL_PARAM_construct_end();
+ if (!evp_keymgmt_get_params(pkey->keymgmt, pkey->keydata, params))
+ return 0;
+ if (params[0].return_size == not_set)
+ return 0;
+ return 1;
+}