ENCODER: Add output structure support for EVP_PKEY encoding
authorRichard Levitte <levitte@openssl.org>
Sat, 17 Oct 2020 06:23:43 +0000 (08:23 +0200)
committerRichard Levitte <levitte@openssl.org>
Wed, 11 Nov 2020 11:43:27 +0000 (12:43 +0100)
OSSL_ENCODER_CTX_new_by_EVP_PKEY() takes one more argument to express
the desired outermost structure for the output.

This also adds OSSL_ENCODER_CTX_prune_encoders(), which is used to
reduce the stack of encoders found according to criteria formed from
the combination of desired selection, output type and output
structure.

squash! ENCODER: Add output structure support for EVP_PKEY encoding

Replace the paragraph talking about OSSL_ENCODER_CTX_prune_encoders() with:

The encoding processor encoder_process() is enhanced with better
analysis of the stack of encoder implementations.  To avoid having to
keep an on the side array of information, it uses recursion.

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/13167)

crypto/encode_decode/encoder_lib.c
crypto/encode_decode/encoder_pkey.c
doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod
include/openssl/encoder.h

index 5e4bf69787222351e79ce1930dc44287d5ec2be3..ba68a0533e6a108dc7c189a636bbaabdc7c1be3f 100644 (file)
 #include <openssl/provider.h>
 #include "encoder_local.h"
 
-static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out);
+struct encoder_process_data_st {
+    OSSL_ENCODER_CTX *ctx;
+
+    /* Current BIO */
+    BIO *bio;
+
+    /* Index of the current encoder instance to be processed */
+    int current_encoder_inst_index;
+
+    /* Processing data passed down through recursion */
+    int level;                   /* Recursion level */
+    OSSL_ENCODER_INSTANCE *next_encoder_inst;
+    int count_output_structure;
+
+    /* Processing data passed up through recursion */
+    OSSL_ENCODER_INSTANCE *prev_encoder_inst;
+    unsigned char *running_output;
+    size_t running_output_length;
+};
+
+static int encoder_process(struct encoder_process_data_st *data);
 
 int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out)
 {
-    return encoder_process(ctx, out);
+    struct encoder_process_data_st data;
+
+    memset(&data, 0, sizeof(data));
+    data.ctx = ctx;
+    data.bio = out;
+    data.current_encoder_inst_index = OSSL_ENCODER_CTX_get_num_encoders(ctx);
+
+    return encoder_process(&data) > 0;
 }
 
 #ifndef OPENSSL_NO_STDIO
@@ -57,7 +84,7 @@ int OSSL_ENCODER_to_data(OSSL_ENCODER_CTX *ctx, unsigned char **pdata,
     int ret = 0;
 
     if (pdata_len == NULL) {
-        ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER);
+        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
 
@@ -336,132 +363,236 @@ OSSL_ENCODER_INSTANCE_get_output_structure(OSSL_ENCODER_INSTANCE *encoder_inst)
     return encoder_inst->output_structure;
 }
 
-static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out)
+static int encoder_process(struct encoder_process_data_st *data)
 {
-    size_t i, end;
-    void *latest_output = NULL;
-    size_t latest_output_length = 0;
-    const char *latest_output_type = NULL;
-    const char *last_input_type = NULL;
-    const char *last_output_structure = NULL;
-    int ok = 0;
-
-    end = OSSL_ENCODER_CTX_get_num_encoders(ctx);
-    for (i = 0; i < end; i++) {
-        OSSL_ENCODER_INSTANCE *encoder_inst =
-            sk_OSSL_ENCODER_INSTANCE_value(ctx->encoder_insts, i);
-        OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
-        void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
-        const char *current_input_type =
-            OSSL_ENCODER_INSTANCE_get_input_type(encoder_inst);
-        const char *current_output_type =
-            OSSL_ENCODER_INSTANCE_get_output_type(encoder_inst);
-        const char *current_output_structure =
-            OSSL_ENCODER_INSTANCE_get_output_structure(encoder_inst);
-        BIO *current_out;
-        BIO *allocated_out = NULL;
-        const void *current_data = NULL;
-        OSSL_PARAM abstract[10];
-        OSSL_PARAM *abstract_p;
-        const OSSL_PARAM *current_abstract = NULL;
-
-        if (latest_output_type == NULL) {
-            /*
-             * This is the first iteration, so we prepare the object to be
-             * encoded
-             */
+    OSSL_ENCODER_INSTANCE *current_encoder_inst = NULL;
+    OSSL_ENCODER *current_encoder = NULL;
+    OSSL_ENCODER_CTX *current_encoder_ctx = NULL;
+    BIO *allocated_out = NULL;
+    const void *original_data = NULL;
+    OSSL_PARAM abstract[10];
+    const OSSL_PARAM *current_abstract = NULL;
+    int i;
+    int ok = -1;  /* -1 signifies that the lookup loop gave nothing */
+    int top = 0;
+
+    if (data->next_encoder_inst == NULL) {
+        /* First iteration, where we prepare for what is to come */
+
+        data->count_output_structure =
+            data->ctx->output_structure == NULL ? -1 : 0;
+        top = 1;
+    }
 
-            current_data = ctx->construct(encoder_inst, ctx->construct_data);
+    for (i = data->current_encoder_inst_index; i-- > 0;) {
+        OSSL_ENCODER *next_encoder = NULL;
+        const char *current_output_type;
+        const char *current_output_structure;
+        struct encoder_process_data_st new_data;
+
+        if (!top)
+            next_encoder =
+                OSSL_ENCODER_INSTANCE_get_encoder(data->next_encoder_inst);
+
+        current_encoder_inst =
+            sk_OSSL_ENCODER_INSTANCE_value(data->ctx->encoder_insts, i);
+        current_encoder =
+            OSSL_ENCODER_INSTANCE_get_encoder(current_encoder_inst);
+        current_encoder_ctx =
+            OSSL_ENCODER_INSTANCE_get_encoder_ctx(current_encoder_inst);
+        current_output_type =
+            OSSL_ENCODER_INSTANCE_get_output_type(current_encoder_inst);
+        current_output_structure =
+            OSSL_ENCODER_INSTANCE_get_output_structure(current_encoder_inst);
+        memset(&new_data, 0, sizeof(new_data));
+        new_data.ctx = data->ctx;
+        new_data.current_encoder_inst_index = i;
+        new_data.next_encoder_inst = current_encoder_inst;
+        new_data.count_output_structure = data->count_output_structure;
+        new_data.level = data->level + 1;
 
-            /* Assume that the constructor recorded an error */
-            if (current_data == NULL)
-                goto loop_end;
+        /*
+         * If this is the top call, we check if the output type of the current
+         * encoder matches the desired output type.
+         * If this isn't the top call, i.e. this is deeper in the recursion,
+         * we instead check if the output type of the current encoder matches
+         * the name of the next encoder (the one found by the parent call).
+         */
+        if (top) {
+            if (data->ctx->output_type != NULL
+                && strcasecmp(current_output_type,
+                              data->ctx->output_type) != 0)
+                continue;
         } else {
-            /*
-             * Check that the latest output type matches the currently
-             * considered encoder
-             */
-            if (!OSSL_ENCODER_is_a(encoder, latest_output_type))
+            if (!OSSL_ENCODER_is_a(next_encoder, current_output_type))
                 continue;
+        }
+
+        /*
+         * If the caller and the current encoder specify an output structure,
+         * Check if they match.  If they do, count the match, otherwise skip
+         * the current encoder.
+         */
+        if (data->ctx->output_structure != NULL
+            && current_output_structure != NULL) {
+            if (strcasecmp(data->ctx->output_structure,
+                           current_output_structure) != 0)
+                continue;
+
+            data->count_output_structure++;
+        }
+
+        /*
+         * Recurse to process the encoder implementations before the current
+         * one.
+         */
+        ok = encoder_process(&new_data);
+
+        data->prev_encoder_inst = new_data.prev_encoder_inst;
+        data->running_output = new_data.running_output;
+        data->running_output_length = new_data.running_output_length;
+
+        /*
+         * ok == -1     means that the recursion call above gave no further
+         *              encoders, and that the one we're currently at should
+         *              be tried.
+         * ok == 0      means that something failed in the recursion call
+         *              above, making the result unsuitable for a chain.
+         *              In this case, we simply continue to try finding a
+         *              suitable encoder at this recursion level.
+         * ok == 1      means that the recursion call was successful, and we
+         *              try to use the result at this recursion level.
+         */
+        if (ok != 0)
+            break;
+    }
 
+    /*
+     * If |i < 0|, we didn't find any useful encoder in this recursion, so
+     * we do the rest of the process only if |i >= 0|.
+     */
+    if (i < 0) {
+        ok = -1;
+    } else {
+        /* Preparations */
+
+        switch (ok) {
+        case 0:
+            break;
+        case -1:
             /*
-             * If there is a latest output type, there should be a latest output
+             * We have reached the beginning of the encoder instance sequence,
+             * so we prepare the object to be encoded.
              */
-            if (!ossl_assert(latest_output != NULL)) {
-                ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR);
-                goto loop_end;
-            }
 
             /*
-             * Create an object abstraction from the latest output, which was
-             * stolen from the previous round.
+             * |data->count_output_structure| is one of these values:
+             *
+             * -1       There is no desired output structure
+             *  0       There is a desired output structure, and it wasn't
+             *          matched by any of the encoder instances that were
+             *          considered
+             * >0       There is a desired output structure, and at least one
+             *          of the encoder instances matched it
              */
-            abstract_p = abstract;
-            if (last_input_type != NULL)
-                *abstract_p++ =
-                    OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
-                                                     (char *)last_input_type, 0);
-            if (last_output_structure != NULL)
-                *abstract_p++ =
-                    OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
-                                                     (char *)last_output_structure,
-                                                     0);
-            *abstract_p++ =
-                OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
-                                                  latest_output,
-                                                  latest_output_length);
-            *abstract_p = OSSL_PARAM_construct_end();
-            current_abstract = abstract;
-        }
+            if (data->count_output_structure == 0)
+                return 0;
 
-        /*
-         * If the desired output type matches the output type of the currently
-         * considered encoder, we're setting up final output.  Otherwise, set
-         * up an intermediary memory output.
-         */
-        if (strcasecmp(ctx->output_type, current_output_type) == 0)
-            current_out = out;
-        else if ((current_out = allocated_out = BIO_new(BIO_s_mem())) == NULL)
-            goto loop_end;     /* Assume BIO_new() recorded an error */
+            original_data =
+                data->ctx->construct(current_encoder_inst,
+                                     data->ctx->construct_data);
+
+            /* Assume that the constructor recorded an error */
+            if (original_data != NULL)
+                ok = 1;
+            else
+                ok = 0;
+            break;
+        case 1:
+            if (!ossl_assert(data->running_output != NULL)) {
+                ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR);
+                ok = 0;
+                break;
+            }
 
-        ok = encoder->encode(encoderctx, (OSSL_CORE_BIO *)current_out,
-                             current_data, current_abstract, ctx->selection,
-                             ossl_pw_passphrase_callback_enc, &ctx->pwdata);
+            {
+                /*
+                 * Create an object abstraction from the latest output, which
+                 * was stolen from the previous round.
+                 */
 
-        if (current_input_type != NULL)
-            last_input_type = current_input_type;
-        if (current_output_structure != NULL)
-            last_output_structure = current_output_structure;
+                OSSL_PARAM *abstract_p = abstract;
+                const char *prev_input_type =
+                    OSSL_ENCODER_INSTANCE_get_input_type(data->prev_encoder_inst);
+                const char *prev_output_structure =
+                    OSSL_ENCODER_INSTANCE_get_output_structure(data->prev_encoder_inst);
+
+                if (prev_input_type != NULL)
+                    *abstract_p++ =
+                        OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+                                                         (char *)prev_input_type, 0);
+                if (prev_output_structure != NULL)
+                    *abstract_p++ =
+                        OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
+                                                         (char *)prev_output_structure,
+                                                         0);
+                *abstract_p++ =
+                    OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
+                                                      data->running_output,
+                                                      data->running_output_length);
+                *abstract_p = OSSL_PARAM_construct_end();
+                current_abstract = abstract;
+            }
+            break;
+        }
 
-        if (!ok)
-            goto loop_end;
+        /* Calling the encoder implementation */
 
-        OPENSSL_free(latest_output);
+        if (ok) {
+            BIO *current_out = NULL;
 
-        /*
-         * Steal the output from the BIO_s_mem, if we did allocate one.
-         * That'll be the data for an object abstraction in the next round.
-         */
-        if (allocated_out != NULL) {
-            BUF_MEM *buf;
-
-            BIO_get_mem_ptr(allocated_out, &buf);
-            latest_output = buf->data;
-            latest_output_length = buf->length;
-            memset(buf, 0, sizeof(*buf));
-            BIO_free(allocated_out);
+            /*
+             * If we're at the last encoder instance to use, we're setting up
+             * final output.  Otherwise, set up an intermediary memory output.
+             */
+            if (top)
+                current_out = data->bio;
+            else if ((current_out = allocated_out = BIO_new(BIO_s_mem()))
+                     == NULL)
+                ok = 0;     /* Assume BIO_new() recorded an error */
+
+            if (ok)
+                ok = current_encoder->encode(current_encoder_ctx,
+                                             (OSSL_CORE_BIO *)current_out,
+                                             original_data, current_abstract,
+                                             data->ctx->selection,
+                                             ossl_pw_passphrase_callback_enc,
+                                             &data->ctx->pwdata);
+
+            data->prev_encoder_inst = current_encoder_inst;
         }
+    }
 
-        latest_output_type = encoder_inst->output_type;
+    /* Cleanup and collecting the result */
 
-     loop_end:
-        if (current_data != NULL)
-            ctx->cleanup(ctx->construct_data);
+    OPENSSL_free(data->running_output);
+    data->running_output = NULL;
 
-        if (ok)
-            break;
+    /*
+     * Steal the output from the BIO_s_mem, if we did allocate one.
+     * That'll be the data for an object abstraction in the next round.
+     */
+    if (allocated_out != NULL) {
+        BUF_MEM *buf;
+
+        BIO_get_mem_ptr(allocated_out, &buf);
+        data->running_output = (unsigned char *)buf->data;
+        data->running_output_length = buf->length;
+        memset(buf, 0, sizeof(*buf));
     }
 
-    OPENSSL_free(latest_output);
+    BIO_free(allocated_out);
+    if (original_data != NULL)
+        data->ctx->cleanup(data->ctx->construct_data);
     return ok;
 }
index b6f4cf777a61d43f08b72091af70f7ce07a2081e..301651b62ac6b2e9ffdebb7552398383aa0fbb75 100644 (file)
@@ -13,6 +13,7 @@
 #include <openssl/params.h>
 #include <openssl/encoder.h>
 #include <openssl/core_names.h>
+#include <openssl/provider.h>
 #include <openssl/safestack.h>
 #include "internal/provider.h"
 #include "internal/property.h"
@@ -70,40 +71,42 @@ int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
  */
 
 struct collected_encoder_st {
+    STACK_OF(OPENSSL_CSTRING) *names;
+    const char *output_structure;
     const char *output_type;
-    STACK_OF(OSSL_ENCODER) *encoders;
+
+    OSSL_ENCODER_CTX *ctx;
+
     int error_occured;
 };
 
 static void collect_encoder(OSSL_ENCODER *encoder, void *arg)
 {
     struct collected_encoder_st *data = arg;
-    OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
-    const char *output_type = NULL;
+    size_t i, end_i;
 
     if (data->error_occured)
         return;
 
-    /*
-     * Ask for the output type.  If the encoder doesn't answer to that,
-     * we refuse it.
-     */
-    params[0] =
-        OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE,
-                                      (char **)&output_type, 0);
-    if (!encoder->get_params(params)
-        || !OSSL_PARAM_modified(&params[0])
-        || output_type == NULL
-        || strcasecmp(output_type, data->output_type) != 0)
+    data->error_occured = 1;     /* Assume the worst */
+
+    if (data->names == NULL)
         return;
 
-    data->error_occured = 1;         /* Assume the worst */
+    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_provider(encoder);
+        void *provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
 
-    if (!OSSL_ENCODER_up_ref(encoder) /* ref++ */)
-        return;
-    if (sk_OSSL_ENCODER_push(data->encoders, encoder) <= 0) {
-        OSSL_ENCODER_free(encoder);  /* ref-- */
-        return;
+        if (!OSSL_ENCODER_is_a(encoder, name)
+            || (encoder->does_selection != NULL
+                && !encoder->does_selection(provctx, data->ctx->selection)))
+            continue;
+
+        /* Only add each encoder implementation once */
+        if (OSSL_ENCODER_CTX_add_encoder(data->ctx, encoder))
+            break;
     }
 
     data->error_occured = 0;         /* All is good now */
@@ -218,11 +221,8 @@ static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx,
     }
 
     if (pkey->keymgmt != NULL) {
-        OSSL_ENCODER *found = NULL;
-        const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(pkey->keymgmt);
         struct collected_encoder_st encoder_data;
         struct collected_names_st keymgmt_data;
-        int i;
 
         if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL) {
             ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
@@ -230,87 +230,28 @@ static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx,
         }
 
         /*
-         * Select the encoder in two steps.  First, collect all encoders
-         * that have the correct output type, as well as all keymgmt names.
+         * Select the first encoder implementations in two steps.
+         * First, collect the keymgmt names, then the encoders that match.
          */
-        encoder_data.output_type = ctx->output_type;
-        encoder_data.encoders = sk_OSSL_ENCODER_new_null();
-        encoder_data.error_occured = 0;
         keymgmt_data.names = sk_OPENSSL_CSTRING_new_null();
         keymgmt_data.error_occured = 0;
-        if (encoder_data.encoders == NULL || keymgmt_data.names == NULL) {
-            sk_OSSL_ENCODER_free(encoder_data.encoders);
-            sk_OPENSSL_CSTRING_free(keymgmt_data.names);
-            return 0;
-        }
-        OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
         EVP_KEYMGMT_names_do_all(pkey->keymgmt, collect_name, &keymgmt_data);
-
-        /*-
-         * Now we look for the most desirable encoder for our |pkey|.
-         *
-         * Encoders offer two functions:
-         *
-         * - one ('encode') that encodes a given provider-native object that
-         *   it knows intimately, so it must be from the same provider.
-         * - one ('import_object') that imports the parameters of an object
-         *   of the same type from a different provider, which is used to
-         *   create a temporary object that 'encode' can handle.
-         *
-         * It is, of course, more desirable to be able to use 'encode'
-         * directly without having to go through an export/import maneuver,
-         * but the latter allows us to have generic encoders.
-         *
-         * Of course, if |libctx| is different from |pkey|'s library context,
-         * we're going to have to do an export/import maneuvre no matter what.
-         */
-        for (i = 0; i < sk_OSSL_ENCODER_num(encoder_data.encoders); i++) {
-            OSSL_ENCODER *encoder =
-                sk_OSSL_ENCODER_value(encoder_data.encoders, i);
-            int j;
-
-            /* Check that any of the |keymgmt| names match */
-            for (j = 0; j < sk_OPENSSL_CSTRING_num(keymgmt_data.names); j++) {
-                const char *name =
-                    sk_OPENSSL_CSTRING_value(keymgmt_data.names, j);
-
-                if (OSSL_ENCODER_is_a(encoder, name))
-                    break;
-            }
-
-            if (j == sk_OPENSSL_CSTRING_num(keymgmt_data.names))
-                continue;
-
-            /* We found one!  Process it */
-            if (OSSL_ENCODER_provider(encoder) == desired_prov) {
-                /*
-                 * We found one in the same provider as the keymgmt.  Choose
-                 * it and stop looking.
-                 */
-                found = encoder;
-                break;
-            }
-            if (found == NULL && encoder->import_object != NULL) {
-                /*
-                 * We found one that's good enough.  Choose it for now, but
-                 * keep looking.
-                 */
-                found = encoder;
-            }
-        }
-
-        if (found != NULL) {
-            (void)OSSL_ENCODER_CTX_add_encoder(ctx, found);
-        } else {
-            if (encoder_data.error_occured)
-                ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
-            else
-                ERR_raise(ERR_LIB_OSSL_ENCODER,
-                          OSSL_ENCODER_R_ENCODER_NOT_FOUND);
+        if (keymgmt_data.error_occured) {
+            sk_OPENSSL_CSTRING_free(keymgmt_data.names);
+            goto err;
         }
 
+        encoder_data.names = keymgmt_data.names;
+        encoder_data.output_type = ctx->output_type;
+        encoder_data.output_structure = ctx->output_structure;
+        encoder_data.error_occured = 0;
+        encoder_data.ctx = ctx;
+        OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
         sk_OPENSSL_CSTRING_free(keymgmt_data.names);
-        sk_OSSL_ENCODER_pop_free(encoder_data.encoders, OSSL_ENCODER_free);
+        if (encoder_data.error_occured) {
+            ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
+            goto err;
+        }
     }
 
     if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0) {
@@ -335,8 +276,9 @@ static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx,
 }
 
 OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                                   const char *output_type,
                                                    int selection,
+                                                   const char *output_type,
+                                                   const char *output_struct,
                                                    OSSL_LIB_CTX *libctx,
                                                    const char *propquery)
 {
@@ -347,6 +289,8 @@ OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
         return NULL;
     }
     if (OSSL_ENCODER_CTX_set_output_type(ctx, output_type)
+        && (output_struct == NULL
+            || OSSL_ENCODER_CTX_set_output_structure(ctx, output_struct))
         && OSSL_ENCODER_CTX_set_selection(ctx, selection)
         && ossl_encoder_ctx_setup_for_EVP_PKEY(ctx, pkey, selection,
                                                libctx, propquery)
index f90d13d551679614e32a698e9273a6aa79506287..97ffaa56cdc1f98ad26e0149ed9753a7abfedbaa 100644 (file)
@@ -15,8 +15,9 @@ OSSL_ENCODER_CTX_set_passphrase_ui
  #include <openssl/encoder.h>
 
  OSSL_ENCODER_CTX *
- OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                  const char *output_type, int selection,
+ OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey, int selection,
+                                  const char *output_type,
+                                  const char *output_structure,
                                   OSSL_LIB_CTX *libctx, const char *propquery);
 
  int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
@@ -48,7 +49,8 @@ L</Selections>.
 Internally, OSSL_ENCODER_CTX_new_by_EVP_PKEY() uses the names from the
 L<EVP_KEYMGMT(3)> implementation associated with I<pkey> to build a list of
 applicable encoder implementations that are used to process the I<pkey> into
-the encoding named by I<output_type>.  All these implementations are
+the encoding named by I<output_type>, with the outermost structure named by
+I<output_structure> if that's relevant.  All these implementations are
 implicitly fetched using I<libctx> and I<propquery>.
 
 If no suitable encoder implementation is found,
index 760ff05c77106637fb11003d1205de4b69aec71e..f50d16517edf5d5fbd71541354b35e34c1a20ab1 100644 (file)
@@ -114,8 +114,9 @@ int OSSL_ENCODER_to_data(OSSL_ENCODER_CTX *ctx, unsigned char **pdata,
  * This is more useful than calling OSSL_ENCODER_CTX_new().
  */
 OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey,
-                                                   const char *output_type,
                                                    int selection,
+                                                   const char *output_type,
+                                                   const char *output_struct,
                                                    OSSL_LIB_CTX *libctx,
                                                    const char *propquery);