KEYMGMT: Add a keydata matching function
[openssl.git] / crypto / evp / pmeth_fn.c
index aa226fcc7b0797d0b1558e8b871b569e7cfae6b3..ca0790fcd627984eb7d618e17cdf14efa8e8e004 100644 (file)
@@ -21,6 +21,8 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation)
     int ret = 0;
     void *provkey = NULL;
     EVP_ASYM_CIPHER *cipher = NULL;
+    EVP_KEYMGMT *tmp_keymgmt = NULL;
+    const char *supported_ciph = NULL;
 
     if (ctx == NULL) {
         EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
@@ -30,56 +32,71 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation)
     evp_pkey_ctx_free_old_ops(ctx);
     ctx->operation = operation;
 
+    /*
+     * TODO when we stop falling back to legacy, this and the ERR_pop_to_mark()
+     * calls can be removed.
+     */
+    ERR_set_mark();
+
     if (ctx->keytype == NULL || ctx->engine != NULL)
         goto legacy;
 
-    if (ctx->keymgmt == NULL)
-        ctx->keymgmt =
-            EVP_KEYMGMT_fetch(ctx->libctx, ctx->keytype, ctx->propquery);
-    if (ctx->keymgmt != NULL) {
-        const char *supported_ciph = NULL;
-
-        if (ctx->keymgmt->query_operation_name != NULL)
-            supported_ciph =
-                ctx->keymgmt->query_operation_name(OSSL_OP_ASYM_CIPHER);
-
-        /*
-         * If we didn't get a supported ciph, assume there is one with the
-         * same name as the key type.
-         */
-        if (supported_ciph == NULL)
-            supported_ciph = ctx->keytype;
-
-        /*
-         * Because we cleared out old ops, we shouldn't need to worry about
-         * checking if cipher is already there.
-         */
-        cipher =
-            EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph, ctx->propquery);
+    /*
+     * Ensure that the key is provided, either natively, or as a cached export.
+     *  If not, go legacy
+     */
+    tmp_keymgmt = ctx->keymgmt;
+    provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
+                                          &tmp_keymgmt, ctx->propquery);
+    if (provkey == NULL)
+        goto legacy;
+    if (!EVP_KEYMGMT_up_ref(tmp_keymgmt)) {
+        ERR_clear_last_mark();
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+        goto err;
     }
-
-    if (ctx->keymgmt == NULL
-        || cipher == NULL
+    EVP_KEYMGMT_free(ctx->keymgmt);
+    ctx->keymgmt = tmp_keymgmt;
+
+    if (ctx->keymgmt->query_operation_name != NULL)
+        supported_ciph =
+            ctx->keymgmt->query_operation_name(OSSL_OP_ASYM_CIPHER);
+
+    /*
+     * If we didn't get a supported ciph, assume there is one with the
+     * same name as the key type.
+     */
+    if (supported_ciph == NULL)
+        supported_ciph = ctx->keytype;
+
+    /*
+     * Because we cleared out old ops, we shouldn't need to worry about
+     * checking if cipher is already there.
+     */
+    cipher =
+        EVP_ASYM_CIPHER_fetch(ctx->libctx, supported_ciph, ctx->propquery);
+
+    if (cipher == NULL
         || (EVP_KEYMGMT_provider(ctx->keymgmt)
             != EVP_ASYM_CIPHER_provider(cipher))) {
         /*
-         * We don't have the full support we need with provided methods,
-         * let's go see if legacy does.  Also, we don't need to free
-         * ctx->keymgmt here, as it's not necessarily tied to this
-         * operation.  It will be freed by EVP_PKEY_CTX_free().
+         * We don't need to free ctx->keymgmt here, as it's not necessarily
+         * tied to this operation.  It will be freed by EVP_PKEY_CTX_free().
          */
         EVP_ASYM_CIPHER_free(cipher);
         goto legacy;
     }
 
-    ctx->op.ciph.cipher = cipher;
+    /*
+     * TODO remove this when legacy is gone
+     * If we don't have the full support we need with provided methods,
+     * let's go see if legacy does.
+     */
+    ERR_pop_to_mark();
 
-    if (ctx->pkey != NULL) {
-        provkey = evp_keymgmt_export_to_provider(ctx->pkey, ctx->keymgmt, 0);
-        /* If export failed, legacy may be able to pick it up */
-        if (provkey == NULL)
-            goto legacy;
-    }
+    /* No more legacy from here down to legacy: */
+
+    ctx->op.ciph.cipher = cipher;
     ctx->op.ciph.ciphprovctx = cipher->newctx(ossl_provider_ctx(cipher->prov));
     if (ctx->op.ciph.ciphprovctx == NULL) {
         /* The provider key can stay in the cache */
@@ -117,6 +134,13 @@ static int evp_pkey_asym_cipher_init(EVP_PKEY_CTX *ctx, int operation)
     return 1;
 
  legacy:
+    /*
+     * TODO remove this when legacy is gone
+     * If we don't have the full support we need with provided methods,
+     * let's go see if legacy does.
+     */
+    ERR_pop_to_mark();
+
     if (ctx->pmeth == NULL || ctx->pmeth->encrypt == NULL) {
         EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
         return -2;