From 762473f53a0cf319e511895556a6df0e3fbb3f9e Mon Sep 17 00:00:00 2001
From: slontis <shane.lontis@oracle.com>
Date: Wed, 5 Oct 2022 09:57:51 +1000
Subject: [PATCH] Improve performance of the encoder collection

Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Paul Dale <pauli@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19344)

(cherry picked from commit c3b46409559c18f103ebb2221c6f8af3cd7db00d)
---
 crypto/encode_decode/encoder_pkey.c | 80 ++++++++++++++++++-----------
 1 file changed, 51 insertions(+), 29 deletions(-)

diff --git a/crypto/encode_decode/encoder_pkey.c b/crypto/encode_decode/encoder_pkey.c
index 3a24317cf4..f8a5a45acc 100644
--- a/crypto/encode_decode/encoder_pkey.c
+++ b/crypto/encode_decode/encoder_pkey.c
@@ -17,6 +17,7 @@
 #include <openssl/trace.h>
 #include "internal/provider.h"
 #include "internal/property.h"
+#include "internal/namemap.h"
 #include "crypto/evp.h"
 #include "encoder_local.h"
 
@@ -72,6 +73,7 @@ int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
 
 struct collected_encoder_st {
     STACK_OF(OPENSSL_CSTRING) *names;
+    int *id_names;
     const char *output_structure;
     const char *output_type;
 
@@ -85,41 +87,41 @@ struct collected_encoder_st {
 static void collect_encoder(OSSL_ENCODER *encoder, void *arg)
 {
     struct collected_encoder_st *data = arg;
-    size_t i, end_i;
+    const OSSL_PROVIDER *prov;
 
     if (data->error_occurred)
         return;
 
     data->error_occurred = 1;     /* Assume the worst */
 
-    if (data->names == NULL)
-        return;
-
-    end_i = sk_OPENSSL_CSTRING_num(data->names);
-    for (i = 0; i < end_i; i++) {
-        const char *name = sk_OPENSSL_CSTRING_value(data->names, i);
-        const OSSL_PROVIDER *prov = OSSL_ENCODER_get0_provider(encoder);
+    prov = OSSL_ENCODER_get0_provider(encoder);
+    /*
+     * collect_encoder() is called in two passes, one where the encoders
+     * from the same provider as the keymgmt are looked up, and one where
+     * the other encoders are looked up.  |data->flag_find_same_provider|
+     * tells us which pass we're in.
+     */
+    if ((data->keymgmt_prov == prov) == data->flag_find_same_provider) {
         void *provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
-
-        /*
-         * collect_encoder() is called in two passes, one where the encoders
-         * from the same provider as the keymgmt are looked up, and one where
-         * the other encoders are looked up.  |data->flag_find_same_provider|
-         * tells us which pass we're in.
-         */
-        if ((data->keymgmt_prov == prov) != data->flag_find_same_provider)
-            continue;
-
-        if (!OSSL_ENCODER_is_a(encoder, name)
-            || (encoder->does_selection != NULL
-                && !encoder->does_selection(provctx, data->ctx->selection))
-            || (data->keymgmt_prov != prov
-                && encoder->import_object == NULL))
-            continue;
-
-        /* Only add each encoder implementation once */
-        if (OSSL_ENCODER_CTX_add_encoder(data->ctx, encoder))
-            break;
+        size_t i, end_i = sk_OPENSSL_CSTRING_num(data->names);
+        int match;
+
+        for (i = 0; i < end_i; i++) {
+            if (data->flag_find_same_provider)
+                match = (data->id_names[i] == encoder->base.id);
+            else
+                match = OSSL_ENCODER_is_a(encoder, sk_OPENSSL_CSTRING_value(data->names, i));
+            if (!match
+                || (encoder->does_selection != NULL
+                    && !encoder->does_selection(provctx, data->ctx->selection))
+                || (data->keymgmt_prov != prov
+                    && encoder->import_object == NULL))
+                continue;
+
+            /* Only add each encoder implementation once */
+            if (OSSL_ENCODER_CTX_add_encoder(data->ctx, encoder))
+                break;
+        }
     }
 
     data->error_occurred = 0;         /* All is good now */
@@ -227,7 +229,8 @@ static int ossl_encoder_ctx_setup_for_pkey(OSSL_ENCODER_CTX *ctx,
     struct construct_data_st *data = NULL;
     const OSSL_PROVIDER *prov = NULL;
     OSSL_LIB_CTX *libctx = NULL;
-    int ok = 0;
+    int ok = 0, i, end;
+    OSSL_NAMEMAP *namemap;
 
     if (!ossl_assert(ctx != NULL) || !ossl_assert(pkey != NULL)) {
         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
@@ -271,7 +274,25 @@ static int ossl_encoder_ctx_setup_for_pkey(OSSL_ENCODER_CTX *ctx,
         encoder_data.error_occurred = 0;
         encoder_data.keymgmt_prov = prov;
         encoder_data.ctx = ctx;
+        encoder_data.id_names = NULL;
 
+        /*
+         * collect_encoder() is called many times, and for every call it converts all encoder_data.names
+         * into namemap ids if it calls OSSL_ENCODER_is_a(). We cache the ids here instead,
+         * and can use them for encoders with the same provider as the keymgmt.
+         */
+        namemap = ossl_namemap_stored(libctx);
+        end = sk_OPENSSL_CSTRING_num(encoder_data.names);
+        if (end > 0) {
+            encoder_data.id_names = OPENSSL_malloc(end * sizeof(int));
+            if (encoder_data.id_names == NULL)
+                goto err;
+            for (i = 0; i < end; ++i) {
+                const char *name = sk_OPENSSL_CSTRING_value(keymgmt_data.names, i);
+
+                encoder_data.id_names[i] = ossl_namemap_name2num(namemap, name);
+            }
+        }
         /*
          * Place the encoders with the a different provider as the keymgmt
          * last (the chain is processed in reverse order)
@@ -286,6 +307,7 @@ static int ossl_encoder_ctx_setup_for_pkey(OSSL_ENCODER_CTX *ctx,
         encoder_data.flag_find_same_provider = 1;
         OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
 
+        OPENSSL_free(encoder_data.id_names);
         sk_OPENSSL_CSTRING_free(keymgmt_data.names);
         if (encoder_data.error_occurred) {
             ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
-- 
2.33.0

