diff options
-rw-r--r-- | crypto/encode_decode/encoder_err.c | 8 | ||||
-rw-r--r-- | crypto/encode_decode/encoder_lib.c | 342 | ||||
-rw-r--r-- | crypto/encode_decode/encoder_local.h | 52 | ||||
-rw-r--r-- | crypto/encode_decode/encoder_meth.c | 122 | ||||
-rw-r--r-- | crypto/encode_decode/encoder_pkey.c | 366 | ||||
-rw-r--r-- | crypto/err/openssl.ec | 4 | ||||
-rw-r--r-- | crypto/err/openssl.txt | 1 | ||||
-rw-r--r-- | doc/man3/OSSL_ENCODER.pod | 15 | ||||
-rw-r--r-- | doc/man3/OSSL_ENCODER_CTX.pod | 177 | ||||
-rw-r--r-- | doc/man3/OSSL_ENCODER_CTX_new_by_EVP_PKEY.pod | 112 | ||||
-rw-r--r-- | doc/man3/OSSL_ENCODER_to_bio.pod | 24 | ||||
-rw-r--r-- | include/crypto/encoder.h | 2 | ||||
-rw-r--r-- | include/openssl/encoder.h | 69 | ||||
-rw-r--r-- | include/openssl/encodererr.h | 5 | ||||
-rw-r--r-- | util/libcrypto.num | 19 | ||||
-rw-r--r-- | util/other.syms | 3 |
16 files changed, 979 insertions, 342 deletions
diff --git a/crypto/encode_decode/encoder_err.c b/crypto/encode_decode/encoder_err.c index 5416f8390e..2c95a2a20e 100644 --- a/crypto/encode_decode/encoder_err.c +++ b/crypto/encode_decode/encoder_err.c @@ -14,10 +14,12 @@ #ifndef OPENSSL_NO_ERR static const ERR_STRING_DATA OSSL_ENCODER_str_reasons[] = { - {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY), - "incorrect property query"}, {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_ENCODER_NOT_FOUND), - "encoder not found"}, + "encoder not found"}, + {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_INCORRECT_PROPERTY_QUERY), + "incorrect property query"}, + {ERR_PACK(ERR_LIB_OSSL_ENCODER, 0, OSSL_ENCODER_R_MISSING_GET_PARAMS), + "missing get params"}, {0, NULL} }; diff --git a/crypto/encode_decode/encoder_lib.c b/crypto/encode_decode/encoder_lib.c index b083fa2d4c..179c6d3dc3 100644 --- a/crypto/encode_decode/encoder_lib.c +++ b/crypto/encode_decode/encoder_lib.c @@ -7,13 +7,20 @@ * https://www.openssl.org/source/license.html */ +#include "e_os.h" /* strcasecmp on Windows */ +#include <openssl/core_names.h> #include <openssl/bio.h> #include <openssl/encoder.h> +#include <openssl/buffer.h> +#include <openssl/params.h> +#include <openssl/provider.h> #include "encoder_local.h" +static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out); + int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out) { - return ctx->do_output(ctx, out); + return encoder_process(ctx, out); } #ifndef OPENSSL_NO_STDIO @@ -41,3 +48,336 @@ int OSSL_ENCODER_to_fp(OSSL_ENCODER_CTX *ctx, FILE *fp) return ret; } #endif + +int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx, + const char *output_type) +{ + if (!ossl_assert(ctx != NULL) || !ossl_assert(output_type != NULL)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + ctx->output_type = output_type; + return 1; +} + +int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (!ossl_assert(selection != 0)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + + ctx->selection = selection; + return 1; +} + +static OSSL_ENCODER_INSTANCE *ossl_encoder_instance_new(OSSL_ENCODER *encoder, + void *encoderctx) +{ + OSSL_ENCODER_INSTANCE *encoder_inst = NULL; + OSSL_PARAM params[3]; + + if (!ossl_assert(encoder != NULL)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (encoder->get_params == NULL) { + ERR_raise(ERR_LIB_OSSL_ENCODER, + OSSL_ENCODER_R_MISSING_GET_PARAMS); + return 0; + } + + if ((encoder_inst = OPENSSL_zalloc(sizeof(*encoder_inst))) == NULL) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); + return 0; + } + + /* + * Cache the input and output types for this encoder. The output type + * is mandatory. + */ + params[0] = + OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE, + (char **)&encoder_inst->output_type, 0); + params[1] = + OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_INPUT_TYPE, + (char **)&encoder_inst->input_type, 0); + params[2] = OSSL_PARAM_construct_end(); + + if (!encoder->get_params(params) + || !OSSL_PARAM_modified(¶ms[1])) + goto err; + + if (!OSSL_ENCODER_up_ref(encoder)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR); + goto err; + } + + encoder_inst->encoder = encoder; + encoder_inst->encoderctx = encoderctx; + return encoder_inst; + err: + ossl_encoder_instance_free(encoder_inst); + return NULL; +} + +void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst) +{ + if (encoder_inst != NULL) { + if (encoder_inst->encoder != NULL) + encoder_inst->encoder->freectx(encoder_inst->encoderctx); + encoder_inst->encoderctx = NULL; + OSSL_ENCODER_free(encoder_inst->encoder); + encoder_inst->encoder = NULL; + OPENSSL_free(encoder_inst); + } +} + +static int ossl_encoder_ctx_add_encoder_inst(OSSL_ENCODER_CTX *ctx, + OSSL_ENCODER_INSTANCE *ei) +{ + if (ctx->encoder_insts == NULL + && (ctx->encoder_insts = + sk_OSSL_ENCODER_INSTANCE_new_null()) == NULL) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); + return 0; + } + + return (sk_OSSL_ENCODER_INSTANCE_push(ctx->encoder_insts, ei) > 0); +} + +int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder) +{ + OSSL_ENCODER_INSTANCE *encoder_inst = NULL; + const OSSL_PROVIDER *prov = NULL; + void *encoderctx = NULL; + void *provctx = NULL; + + if (!ossl_assert(ctx != NULL) || !ossl_assert(encoder != NULL)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + prov = OSSL_ENCODER_provider(encoder); + provctx = OSSL_PROVIDER_get0_provider_ctx(prov); + + if ((encoderctx = encoder->newctx(provctx)) == NULL + || (encoder_inst = + ossl_encoder_instance_new(encoder, encoderctx)) == NULL) + goto err; + /* Avoid double free of encoderctx on further errors */ + encoderctx = NULL; + + if (!ossl_encoder_ctx_add_encoder_inst(ctx, encoder_inst)) + goto err; + + return 1; + err: + ossl_encoder_instance_free(encoder_inst); + if (encoderctx != NULL) + encoder->freectx(encoderctx); + return 0; +} + +int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx, + OPENSSL_CTX *libctx, const char *propq) +{ + return 1; +} + +int OSSL_ENCODER_CTX_get_num_encoders(OSSL_ENCODER_CTX *ctx) +{ + if (ctx == NULL || ctx->encoder_insts == NULL) + return 0; + return sk_OSSL_ENCODER_INSTANCE_num(ctx->encoder_insts); +} + +int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx, + OSSL_ENCODER_CONSTRUCT *construct) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + ctx->construct = construct; + return 1; +} + +int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx, + void *construct_data) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + ctx->construct_data = construct_data; + return 1; +} + +int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx, + OSSL_ENCODER_CLEANUP *cleanup) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + ctx->cleanup = cleanup; + return 1; +} + +OSSL_ENCODER * +OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst) +{ + if (encoder_inst == NULL) + return NULL; + return encoder_inst->encoder; +} + +void * +OSSL_ENCODER_INSTANCE_get_encoder_ctx(OSSL_ENCODER_INSTANCE *encoder_inst) +{ + if (encoder_inst == NULL) + return NULL; + return encoder_inst->encoderctx; +} + +const char * +OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst) +{ + if (encoder_inst == NULL) + return NULL; + return encoder_inst->input_type; +} + +const char * +OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst) +{ + if (encoder_inst == NULL) + return NULL; + return encoder_inst->output_type; +} + +static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out) +{ + 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; + 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); + BIO *current_out; + BIO *allocated_out = NULL; + const void *current_data = NULL; + OSSL_PARAM abstract[3]; + 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 + */ + + current_data = ctx->construct(encoder_inst, ctx->construct_data); + + /* Assume that the constructor recorded an error */ + if (current_data == NULL) + goto loop_end; + } else { + /* + * Check that the latest output type matches the currently + * considered encoder + */ + if (!OSSL_ENCODER_is_a(encoder, latest_output_type)) + continue; + + /* + * If there is a latest output type, there should be a latest output + */ + 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. + */ + 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); + *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 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 */ + + ok = encoder->encode(encoderctx, (OSSL_CORE_BIO *)current_out, + current_data, current_abstract, ctx->selection, + ossl_pw_passphrase_callback_enc, &ctx->pwdata); + + if (current_input_type != NULL) + last_input_type = current_input_type; + + if (!ok) + goto loop_end; + + OPENSSL_free(latest_output); + + /* + * 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); + } + + loop_end: + if (current_data != NULL) + ctx->cleanup(ctx->construct_data); + + if (ok) + break; + } + + OPENSSL_free(latest_output); + return ok; +} diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h index e707be19ff..a57d0cd16c 100644 --- a/crypto/encode_decode/encoder_local.h +++ b/crypto/encode_decode/encoder_local.h @@ -29,10 +29,13 @@ struct ossl_encoder_st { struct ossl_endecode_base_st base; OSSL_FUNC_encoder_newctx_fn *newctx; OSSL_FUNC_encoder_freectx_fn *freectx; + OSSL_FUNC_encoder_get_params_fn *get_params; + OSSL_FUNC_encoder_gettable_params_fn *gettable_params; OSSL_FUNC_encoder_set_ctx_params_fn *set_ctx_params; OSSL_FUNC_encoder_settable_ctx_params_fn *settable_ctx_params; - OSSL_FUNC_encoder_encode_data_fn *encode_data; - OSSL_FUNC_encoder_encode_object_fn *encode_object; + OSSL_FUNC_encoder_encode_fn *encode; + OSSL_FUNC_encoder_import_object_fn *import_object; + OSSL_FUNC_encoder_free_object_fn *free_object; }; struct ossl_decoder_st { @@ -47,22 +50,43 @@ struct ossl_decoder_st { OSSL_FUNC_decoder_export_object_fn *export_object; }; -struct ossl_encoder_ctx_st { - OSSL_ENCODER *encoder; - void *encoderctx; +struct ossl_encoder_instance_st { + OSSL_ENCODER *encoder; /* Never NULL */ + void *encoderctx; /* Never NULL */ + const char *input_type; /* May be NULL */ + const char *output_type; /* Never NULL */ +}; + +DEFINE_STACK_OF(OSSL_ENCODER_INSTANCE) + +void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst); +struct ossl_encoder_ctx_st { + /* + * The desired output type. The encoder implementation have a gettable + * "output-type" parameter that this will match against. + */ + const char *output_type; + /* + * Select what parts of an object will be encoded. This selection is + * bit encoded, and the bits correspond to selection bits available with + * the provider side operation. For example, when encoding an EVP_PKEY, + * the OSSL_KEYMGMT_SELECT_ macros are used for this. + */ int selection; - /*- - * Output / encoding data, used by OSSL_ENCODER_to_{bio,fp} - * - * |object| is the libcrypto object to handle. - * |do_output| performs the actual encoding. - * - * |do_output| must have intimate knowledge of |object|. + /* + * Decoders that are components of any current decoding path. */ - const void *object; - int (*do_output)(OSSL_ENCODER_CTX *ctx, BIO *out); + STACK_OF(OSSL_ENCODER_INSTANCE) *encoder_insts; + + /* + * The constructor and destructor of an object to pass to the first + * encoder in a chain. + */ + OSSL_ENCODER_CONSTRUCT *construct; + OSSL_ENCODER_CLEANUP *cleanup; + void *construct_data; /* For any function that needs a passphrase reader */ struct ossl_passphrase_data_st pwdata; diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c index 62342f511a..93929b5360 100644 --- a/crypto/encode_decode/encoder_meth.c +++ b/crypto/encode_decode/encoder_meth.c @@ -180,6 +180,16 @@ static void *encoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef, encoder->freectx = OSSL_FUNC_encoder_freectx(fns); break; + case OSSL_FUNC_ENCODER_GET_PARAMS: + if (encoder->get_params == NULL) + encoder->get_params = + OSSL_FUNC_encoder_get_params(fns); + break; + case OSSL_FUNC_ENCODER_GETTABLE_PARAMS: + if (encoder->gettable_params == NULL) + encoder->gettable_params = + OSSL_FUNC_encoder_gettable_params(fns); + break; case OSSL_FUNC_ENCODER_SET_CTX_PARAMS: if (encoder->set_ctx_params == NULL) encoder->set_ctx_params = @@ -190,26 +200,34 @@ static void *encoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef, encoder->settable_ctx_params = OSSL_FUNC_encoder_settable_ctx_params(fns); break; - case OSSL_FUNC_ENCODER_ENCODE_DATA: - if (encoder->encode_data == NULL) - encoder->encode_data = - OSSL_FUNC_encoder_encode_data(fns); + case OSSL_FUNC_ENCODER_ENCODE: + if (encoder->encode == NULL) + encoder->encode = OSSL_FUNC_encoder_encode(fns); + break; + case OSSL_FUNC_ENCODER_IMPORT_OBJECT: + if (encoder->import_object == NULL) + encoder->import_object = + OSSL_FUNC_encoder_import_object(fns); break; - case OSSL_FUNC_ENCODER_ENCODE_OBJECT: - if (encoder->encode_object == NULL) - encoder->encode_object = - OSSL_FUNC_encoder_encode_object(fns); + case OSSL_FUNC_ENCODER_FREE_OBJECT: + if (encoder->free_object == NULL) + encoder->free_object = + OSSL_FUNC_encoder_free_object(fns); break; } } /* * Try to check that the method is sensible. * If you have a constructor, you must have a destructor and vice versa. - * You must have at least one of the encoding driver functions. + * You must have the encoding driver functions. */ if (!((encoder->newctx == NULL && encoder->freectx == NULL) - || (encoder->newctx != NULL && encoder->freectx != NULL)) - || (encoder->encode_data == NULL && encoder->encode_object == NULL)) { + || (encoder->newctx != NULL && encoder->freectx != NULL) + || (encoder->import_object != NULL && encoder->free_object != NULL) + || (encoder->import_object == NULL && encoder->free_object == NULL)) + || encoder->encode == NULL + || encoder->gettable_params == NULL + || encoder->get_params == NULL) { OSSL_ENCODER_free(encoder); ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INVALID_PROVIDER_FUNCTIONS); return NULL; @@ -440,6 +458,24 @@ void OSSL_ENCODER_names_do_all(const OSSL_ENCODER *encoder, } } +const OSSL_PARAM * +OSSL_ENCODER_gettable_params(OSSL_ENCODER *encoder) +{ + if (encoder != NULL && encoder->gettable_params != NULL) { + void *provctx = ossl_provider_ctx(OSSL_ENCODER_provider(encoder)); + + return encoder->gettable_params(provctx); + } + return NULL; +} + +int OSSL_ENCODER_get_params(OSSL_ENCODER *encoder, OSSL_PARAM params[]) +{ + if (encoder != NULL && encoder->get_params != NULL) + return encoder->get_params(params); + return 0; +} + const OSSL_PARAM *OSSL_ENCODER_settable_ctx_params(OSSL_ENCODER *encoder) { if (encoder != NULL && encoder->settable_ctx_params != NULL) { @@ -454,69 +490,51 @@ const OSSL_PARAM *OSSL_ENCODER_settable_ctx_params(OSSL_ENCODER *encoder) * Encoder context support */ -/* - * |encoder| value NULL is valid, and signifies that there is no encoder. - * This is useful to provide fallback mechanisms. - * Functions that want to verify if there is a encoder can do so with - * OSSL_ENCODER_CTX_get_encoder() - */ -OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new(OSSL_ENCODER *encoder) +OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new(void) { OSSL_ENCODER_CTX *ctx; - if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) { + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); - return NULL; - } - - ctx->encoder = encoder; - if (encoder != NULL && encoder->newctx != NULL) { - const OSSL_PROVIDER *prov = OSSL_ENCODER_provider(encoder); - void *provctx = ossl_provider_ctx(prov); - - if (OSSL_ENCODER_up_ref(encoder)) { - ctx->encoderctx = encoder->newctx(provctx); - } else { - OSSL_ENCODER_free(encoder); - OPENSSL_free(ctx); - ctx = NULL; - } - } return ctx; } -const OSSL_ENCODER * -OSSL_ENCODER_CTX_get_encoder(OSSL_ENCODER_CTX *ctx) +int OSSL_ENCODER_CTX_set_params(OSSL_ENCODER_CTX *ctx, + const OSSL_PARAM params[]) { + size_t i; + size_t l; + if (!ossl_assert(ctx != NULL)) { ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); return 0; } - return ctx->encoder; -} + if (ctx->encoder_insts == NULL) + return 1; + l = OSSL_ENCODER_CTX_get_num_encoders(ctx); + for (i = 0; i < l; 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); -int OSSL_ENCODER_CTX_set_params(OSSL_ENCODER_CTX *ctx, - const OSSL_PARAM params[]) -{ - if (!ossl_assert(ctx != NULL)) { - ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); - return 0; + if (encoderctx == NULL || encoder->set_ctx_params == NULL) + continue; + if (!encoder->set_ctx_params(encoderctx, params)) + return 0; } - - if (ctx->encoder != NULL && ctx->encoder->set_ctx_params != NULL) - return ctx->encoder->set_ctx_params(ctx->encoderctx, params); - return 0; + return 1; } void OSSL_ENCODER_CTX_free(OSSL_ENCODER_CTX *ctx) { if (ctx != NULL) { - if (ctx->encoder != NULL && ctx->encoder->freectx != NULL) - ctx->encoder->freectx(ctx->encoderctx); - OSSL_ENCODER_free(ctx->encoder); + sk_OSSL_ENCODER_INSTANCE_pop_free(ctx->encoder_insts, + ossl_encoder_instance_free); + OPENSSL_free(ctx->construct_data); ossl_pw_clear_passphrase_data(&ctx->pwdata); OPENSSL_free(ctx); } diff --git a/crypto/encode_decode/encoder_pkey.c b/crypto/encode_decode/encoder_pkey.c index 7c63a76adb..6e1a80da9f 100644 --- a/crypto/encode_decode/encoder_pkey.c +++ b/crypto/encode_decode/encoder_pkey.c @@ -7,6 +7,7 @@ * https://www.openssl.org/source/license.html */ +#include "e_os.h" /* strcasecmp on Windows */ #include <openssl/err.h> #include <openssl/ui.h> #include <openssl/params.h> @@ -18,6 +19,8 @@ #include "crypto/evp.h" #include "encoder_local.h" +DEFINE_STACK_OF(OSSL_ENCODER) + int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx, const char *cipher_name, const char *propquery) @@ -48,28 +51,82 @@ int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx, return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data); } -int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx, - pem_password_cb *cb, void *cbarg) +int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX *ctx, + pem_password_cb *cb, void *cbarg) { return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg); } +int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx, + OSSL_PASSPHRASE_CALLBACK *cb, + void *cbarg) +{ + return ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg); +} + /* * Support for OSSL_ENCODER_CTX_new_by_TYPE: * finding a suitable encoder */ -struct selected_encoder_st { +struct collected_encoder_st { + const char *output_type; + STACK_OF(OSSL_ENCODER) *encoders; + 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; + + 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(¶ms[0]) + || output_type == NULL + || strcasecmp(output_type, data->output_type) != 0) + return; + + data->error_occured = 1; /* Assume the worst */ + + if (!OSSL_ENCODER_up_ref(encoder) /* ref++ */) + return; + if (sk_OSSL_ENCODER_push(data->encoders, encoder) <= 0) { + OSSL_ENCODER_free(encoder); /* ref-- */ + return; + } + + data->error_occured = 0; /* All is good now */ +} + +struct collected_names_st { STACK_OF(OPENSSL_CSTRING) *names; - int error; + unsigned int error_occured:1; }; -static void cache_encoders(const char *name, void *data) +static void collect_name(const char *name, void *arg) { - struct selected_encoder_st *d = data; + struct collected_names_st *data = arg; + + if (data->error_occured) + return; + + data->error_occured = 1; /* Assume the worst */ - if (sk_OPENSSL_CSTRING_push(d->names, name) <= 0) - d->error = 1; + if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0) + return; + + data->error_occured = 0; /* All is good now */ } /* @@ -78,167 +135,224 @@ static void cache_encoders(const char *name, void *data) * intimate knowledge of the provider side object) */ -struct encoder_write_data_st { - OSSL_ENCODER_CTX *ctx; - BIO *out; +struct construct_data_st { + const EVP_PKEY *pk; + int selection; + + OSSL_ENCODER_INSTANCE *encoder_inst; + const void *obj; + void *constructed_obj; }; -static int encoder_write_cb(const OSSL_PARAM params[], void *arg) +static int encoder_import_cb(const OSSL_PARAM params[], void *arg) { - struct encoder_write_data_st *write_data = arg; - OSSL_ENCODER_CTX *ctx = write_data->ctx; - BIO *out = write_data->out; - - return ctx->encoder->encode_data(ctx->encoderctx, params, - (OSSL_CORE_BIO *)out, - ossl_pw_passphrase_callback_enc, - &ctx->pwdata); -} + struct construct_data_st *construct_data = arg; + OSSL_ENCODER_INSTANCE *encoder_inst = construct_data->encoder_inst; + OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst); + void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst); -/* - * Support for OSSL_ENCODER_to_bio: - * Perform the actual output. - */ + construct_data->constructed_obj = + encoder->import_object(encoderctx, construct_data->selection, params); -static int encoder_EVP_PKEY_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out) + return (construct_data->constructed_obj != NULL); +} + +static const void * +encoder_construct_EVP_PKEY(OSSL_ENCODER_INSTANCE *encoder_inst, void *arg) { - const EVP_PKEY *pkey = ctx->object; - void *keydata = pkey->keydata; - EVP_KEYMGMT *keymgmt = pkey->keymgmt; + struct construct_data_st *data = arg; + + if (data->obj == NULL) { + OSSL_ENCODER *encoder = + OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst); + const EVP_PKEY *pk = data->pk; + const OSSL_PROVIDER *k_prov = EVP_KEYMGMT_provider(pk->keymgmt); + const OSSL_PROVIDER *e_prov = OSSL_ENCODER_provider(encoder); + + if (k_prov != e_prov) { + data->encoder_inst = encoder_inst; + + if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, data->selection, + &encoder_import_cb, data)) + return NULL; + data->obj = data->constructed_obj; + } else { + data->obj = pk->keydata; + } + } - /* - * OSSL_ENCODER_CTX_new() creates a context, even when the - * encoder it's given is NULL. Callers can detect the lack - * of encoder with OSSL_ENCODER_CTX_get_encoder() and - * should take precautions, possibly call a fallback instead of - * OSSL_ENCODER_to_bio() / OSSL_ENCODER_to_fp(). If it's - * come this far, we return an error. - */ - if (ctx->encoder == NULL) - return 0; + return data->obj; +} - if (ctx->encoder->encode_object == NULL - || (OSSL_ENCODER_provider(ctx->encoder) - != EVP_KEYMGMT_provider(keymgmt))) { - struct encoder_write_data_st write_data; +static void encoder_destruct_EVP_PKEY(void *arg) +{ + struct construct_data_st *data = arg; - write_data.ctx = ctx; - write_data.out = out; + if (data->encoder_inst != NULL) { + OSSL_ENCODER *encoder = + OSSL_ENCODER_INSTANCE_get_encoder(data->encoder_inst); - return evp_keymgmt_export(keymgmt, keydata, ctx->selection, - &encoder_write_cb, &write_data); + encoder->free_object(data->constructed_obj); } - - return ctx->encoder->encode_object(ctx->encoderctx, keydata, - (OSSL_CORE_BIO *)out, - ossl_pw_passphrase_callback_enc, - &ctx->pwdata); + data->constructed_obj = NULL; } /* * OSSL_ENCODER_CTX_new_by_EVP_PKEY() returns a ctx with no encoder if * it couldn't find a suitable encoder. This allows a caller to detect if - * a suitable encoder was found, with OSSL_ENCODER_CTX_get_encoder(), + * a suitable encoder was found, with OSSL_ENCODER_CTX_get_num_encoder(), * and to use fallback methods if the result is NULL. */ -OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey, - const char *propquery) +static int ossl_encoder_ctx_setup_for_EVP_PKEY(OSSL_ENCODER_CTX *ctx, + const EVP_PKEY *pkey, + int selection, + OPENSSL_CTX *libctx, + const char *propquery) { - OSSL_ENCODER_CTX *ctx = NULL; - OSSL_ENCODER *encoder = NULL; - EVP_KEYMGMT *keymgmt = pkey->keymgmt; - int selection = OSSL_KEYMGMT_SELECT_ALL; + struct construct_data_st *data = NULL; + int ok = 0; - if (!ossl_assert(pkey != NULL && propquery != NULL)) { + if (!ossl_assert(ctx != NULL) || !ossl_assert(pkey != NULL)) { ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); - return NULL; + return 0; } - if (keymgmt != NULL) { - const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(keymgmt); - OPENSSL_CTX *libctx = ossl_provider_library_context(desired_prov); - struct selected_encoder_st sel_data; - OSSL_ENCODER *first = NULL; - const char *name; + 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 |