+
+#endif /* FIPS_MODE */
+
+/*- All methods below can also be used in FIPS_MODE */
+
+EVP_PKEY *EVP_PKEY_new(void)
+{
+ EVP_PKEY *ret = OPENSSL_zalloc(sizeof(*ret));
+
+ if (ret == NULL) {
+ EVPerr(EVP_F_EVP_PKEY_NEW, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ ret->type = EVP_PKEY_NONE;
+ ret->save_type = EVP_PKEY_NONE;
+ ret->references = 1;
+ ret->save_parameters = 1;
+ ret->lock = CRYPTO_THREAD_lock_new();
+ if (ret->lock == NULL) {
+ EVPerr(EVP_F_EVP_PKEY_NEW, ERR_R_MALLOC_FAILURE);
+ OPENSSL_free(ret);
+ return NULL;
+ }
+ return ret;
+}
+
+int EVP_PKEY_up_ref(EVP_PKEY *pkey)
+{
+ int i;
+
+ if (CRYPTO_UP_REF(&pkey->references, &i, pkey->lock) <= 0)
+ return 0;
+
+ REF_PRINT_COUNT("EVP_PKEY", pkey);
+ REF_ASSERT_ISNT(i < 2);
+ return ((i > 1) ? 1 : 0);
+}
+
+#ifndef FIPS_MODE
+static void evp_pkey_free_legacy(EVP_PKEY *x)
+{
+ if (x->ameth != NULL) {
+ if (x->ameth->pkey_free)
+ x->ameth->pkey_free(x);
+ x->pkey.ptr = NULL;
+ x->ameth = NULL;
+ }
+# ifndef OPENSSL_NO_ENGINE
+ ENGINE_finish(x->engine);
+ x->engine = NULL;
+ ENGINE_finish(x->pmeth_engine);
+ x->pmeth_engine = NULL;
+# endif
+ x->type = x->save_type = EVP_PKEY_NONE;
+}
+#endif /* FIPS_MODE */
+
+static void evp_pkey_free_it(EVP_PKEY *x)
+{
+ /* internal function; x is never NULL */
+
+ evp_keymgmt_util_clear_operation_cache(x);
+#ifndef FIPS_MODE
+ evp_pkey_free_legacy(x);
+#endif
+
+ if (x->keymgmt != NULL) {
+ evp_keymgmt_freedata(x->keymgmt, x->keydata);
+ EVP_KEYMGMT_free(x->keymgmt);
+ x->keymgmt = NULL;
+ x->keydata = NULL;
+ }
+}
+
+void EVP_PKEY_free(EVP_PKEY *x)
+{
+ int i;
+
+ if (x == NULL)
+ return;
+
+ CRYPTO_DOWN_REF(&x->references, &i, x->lock);
+ REF_PRINT_COUNT("EVP_PKEY", x);
+ if (i > 0)
+ return;
+ REF_ASSERT_ISNT(i < 0);
+ evp_pkey_free_it(x);
+ CRYPTO_THREAD_lock_free(x->lock);
+#ifndef FIPS_MODE
+ sk_X509_ATTRIBUTE_pop_free(x->attributes, X509_ATTRIBUTE_free);
+#endif
+ OPENSSL_free(x);
+}
+
+int EVP_PKEY_size(const EVP_PKEY *pkey)
+{
+ if (pkey != NULL) {
+ if (pkey->ameth == NULL)
+ return pkey->cache.size;
+ else if (pkey->ameth->pkey_size != NULL)
+ return pkey->ameth->pkey_size(pkey);
+ }
+ return 0;
+}
+
+void *evp_pkey_export_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
+ EVP_KEYMGMT **keymgmt,
+ const char *propquery)
+{
+ EVP_KEYMGMT *allocated_keymgmt = NULL;
+ EVP_KEYMGMT *tmp_keymgmt = NULL;
+ void *keydata = NULL;
+
+ if (pk == NULL)
+ return NULL;
+
+#ifndef FIPS_MODE
+ if (pk->pkey.ptr != NULL) {
+ /*
+ * If the legacy key doesn't have an dirty counter or export function,
+ * give up
+ */
+ if (pk->ameth->dirty_cnt == NULL || pk->ameth->export_to == NULL)
+ return NULL;
+ }
+#endif
+
+ if (keymgmt != NULL) {
+ tmp_keymgmt = *keymgmt;
+ *keymgmt = NULL;
+ }
+
+ /* If no keymgmt was given or found, get a default keymgmt */
+ if (tmp_keymgmt == NULL) {
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery);
+
+ if (ctx != NULL && ctx->keytype != NULL)
+ tmp_keymgmt = allocated_keymgmt =
+ EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, propquery);
+ EVP_PKEY_CTX_free(ctx);
+ }
+
+ /* If there's still no keymgmt to be had, give up */
+ if (tmp_keymgmt == NULL)
+ goto end;
+
+#ifndef FIPS_MODE
+ if (pk->pkey.ptr != NULL) {
+ size_t i = 0;
+
+ /*
+ * If the legacy "origin" hasn't changed since last time, we try
+ * to find our keymgmt in the operation cache. If it has changed,
+ * |i| remains zero, and we will clear the cache further down.
+ */
+ if (pk->ameth->dirty_cnt(pk) == pk->dirty_cnt_copy) {
+ i = evp_keymgmt_util_find_operation_cache_index(pk, tmp_keymgmt);
+
+ /*
+ * If |tmp_keymgmt| is present in the operation cache, it means
+ * that export doesn't need to be redone. In that case, we take
+ * token copies of the cached pointers, to have token success
+ * values to return.
+ */
+ if (i < OSSL_NELEM(pk->operation_cache)
+ && pk->operation_cache[i].keymgmt != NULL) {
+ keydata = pk->operation_cache[i].keydata;
+ goto end;
+ }
+ }
+
+ /*
+ * TODO(3.0) Right now, we assume we have ample space. We will have
+ * to think about a cache aging scheme, though, if |i| indexes outside
+ * the array.
+ */
+ if (!ossl_assert(i < OSSL_NELEM(pk->operation_cache)))
+ goto end;
+
+ /* Make sure that the keymgmt key type matches the legacy NID */
+ if (!ossl_assert(EVP_KEYMGMT_is_a(tmp_keymgmt, OBJ_nid2sn(pk->type))))
+ goto end;
+
+ if ((keydata = evp_keymgmt_newdata(tmp_keymgmt)) == NULL)
+ goto end;
+
+ if (!pk->ameth->export_to(pk, keydata, tmp_keymgmt)) {
+ evp_keymgmt_freedata(tmp_keymgmt, keydata);
+ keydata = NULL;
+ goto end;
+ }
+
+ /*
+ * If the dirty counter changed since last time, then clear the
+ * operation cache. In that case, we know that |i| is zero. Just
+ * in case this is a re-export, we increment then decrement the
+ * keymgmt reference counter.
+ */
+ if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) { /* refcnt++ */
+ evp_keymgmt_freedata(tmp_keymgmt, keydata);
+ keydata = NULL;
+ goto end;
+ }
+ if (pk->ameth->dirty_cnt(pk) != pk->dirty_cnt_copy)
+ evp_keymgmt_util_clear_operation_cache(pk);
+ EVP_KEYMGMT_free(tmp_keymgmt); /* refcnt-- */
+
+ /* Add the new export to the operation cache */
+ if (!evp_keymgmt_util_cache_keydata(pk, i, tmp_keymgmt, keydata)) {
+ evp_keymgmt_freedata(tmp_keymgmt, keydata);
+ keydata = NULL;
+ goto end;
+ }
+
+ /* Synchronize the dirty count */
+ pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
+ goto end;
+ }
+#endif /* FIPS_MODE */
+
+ keydata = evp_keymgmt_util_export_to_provider(pk, tmp_keymgmt);
+
+ end:
+ /*
+ * If nothing was exported, |tmp_keymgmt| might point at a freed
+ * EVP_KEYMGMT, so we clear it to be safe. It shouldn't be useful for
+ * the caller either way in that case.
+ */
+ if (keydata == NULL)
+ tmp_keymgmt = NULL;
+
+ if (keymgmt != NULL)
+ *keymgmt = tmp_keymgmt;
+
+ EVP_KEYMGMT_free(allocated_keymgmt);
+ return keydata;
+}
+
+#ifndef FIPS_MODE
+/*
+ * This differs from exporting in that it releases the legacy key and assigns
+ * the export keymgmt and keydata to the "origin" provider side key instead
+ * of the operation cache.
+ */
+void *evp_pkey_upgrade_to_provider(EVP_PKEY *pk, OPENSSL_CTX *libctx,
+ EVP_KEYMGMT **keymgmt,
+ const char *propquery)
+{
+ EVP_KEYMGMT *allocated_keymgmt = NULL;
+ EVP_KEYMGMT *tmp_keymgmt = NULL;
+ void *keydata = NULL;
+
+ if (pk == NULL)
+ return NULL;
+
+ /*
+ * If this key is already "upgraded", this function shouldn't have been
+ * called.
+ */
+ if (!ossl_assert(pk->keymgmt == NULL))
+ return NULL;
+
+ if (keymgmt != NULL) {
+ tmp_keymgmt = *keymgmt;
+ *keymgmt = NULL;
+ }
+
+ /* If the key isn't a legacy one, bail out, but with proper values */
+ if (pk->pkey.ptr == NULL) {
+ tmp_keymgmt = pk->keymgmt;
+ keydata = pk->keydata;
+ } else {
+ /* If the legacy key doesn't have an export function, give up */
+ if (pk->ameth->export_to == NULL)
+ return NULL;
+
+ /* If no keymgmt was given, get a default keymgmt */
+ if (tmp_keymgmt == NULL) {
+ EVP_PKEY_CTX *ctx =
+ EVP_PKEY_CTX_new_from_pkey(libctx, pk, propquery);
+
+ if (ctx != NULL && ctx->keytype != NULL)
+ tmp_keymgmt = allocated_keymgmt =
+ EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, propquery);
+ EVP_PKEY_CTX_free(ctx);
+ }
+
+ /* If we still don't have a keymgmt, give up */
+ if (tmp_keymgmt == NULL)
+ goto end;
+
+ /* Make sure that the keymgmt key type matches the legacy NID */
+ if (!ossl_assert(EVP_KEYMGMT_is_a(tmp_keymgmt, OBJ_nid2sn(pk->type))))
+ goto end;
+
+ if ((keydata = evp_keymgmt_newdata(tmp_keymgmt)) == NULL)
+ goto end;
+
+ if (!pk->ameth->export_to(pk, keydata, tmp_keymgmt)
+ || !EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+ evp_keymgmt_freedata(tmp_keymgmt, keydata);
+ keydata = NULL;
+ goto end;
+ }
+
+ /*
+ * Clear the operation cache, all the legacy data, as well as the
+ * dirty counters
+ */
+ evp_pkey_free_legacy(pk);
+ pk->dirty_cnt_copy = 0;
+
+ evp_keymgmt_util_clear_operation_cache(pk);
+ pk->keymgmt = tmp_keymgmt;
+ pk->keydata = keydata;
+ evp_keymgmt_util_cache_keyinfo(pk);
+ }
+
+ end:
+ /*
+ * If nothing was upgraded, |tmp_keymgmt| might point at a freed
+ * EVP_KEYMGMT, so we clear it to be safe. It shouldn't be useful for
+ * the caller either way in that case.
+ */
+ if (keydata == NULL)
+ tmp_keymgmt = NULL;
+
+ if (keymgmt != NULL)
+ *keymgmt = tmp_keymgmt;
+
+ EVP_KEYMGMT_free(allocated_keymgmt);
+ return keydata;
+}
+#endif /* FIPS_MODE */