From ece9304c96f71277ca95696d9bc49fdec51e9f17 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Sun, 16 Aug 2020 21:25:08 +0200 Subject: Rename OSSL_SERIALIZER / OSSL_DESERIALIZER to OSSL_ENCODE / OSSL_DECODE Fixes #12455 Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/12660) --- crypto/encode_decode/build.info | 8 + crypto/encode_decode/decoder_err.c | 31 ++ crypto/encode_decode/decoder_lib.c | 483 ++++++++++++++++++++++++++++++ crypto/encode_decode/decoder_meth.c | 552 +++++++++++++++++++++++++++++++++++ crypto/encode_decode/decoder_pkey.c | 374 ++++++++++++++++++++++++ crypto/encode_decode/encoder_err.c | 33 +++ crypto/encode_decode/encoder_lib.c | 43 +++ crypto/encode_decode/encoder_local.h | 140 +++++++++ crypto/encode_decode/encoder_meth.c | 523 +++++++++++++++++++++++++++++++++ crypto/encode_decode/encoder_pkey.c | 276 ++++++++++++++++++ crypto/encode_decode/endecode_pass.c | 162 ++++++++++ 11 files changed, 2625 insertions(+) create mode 100644 crypto/encode_decode/build.info create mode 100644 crypto/encode_decode/decoder_err.c create mode 100644 crypto/encode_decode/decoder_lib.c create mode 100644 crypto/encode_decode/decoder_meth.c create mode 100644 crypto/encode_decode/decoder_pkey.c create mode 100644 crypto/encode_decode/encoder_err.c create mode 100644 crypto/encode_decode/encoder_lib.c create mode 100644 crypto/encode_decode/encoder_local.h create mode 100644 crypto/encode_decode/encoder_meth.c create mode 100644 crypto/encode_decode/encoder_pkey.c create mode 100644 crypto/encode_decode/endecode_pass.c (limited to 'crypto/encode_decode') diff --git a/crypto/encode_decode/build.info b/crypto/encode_decode/build.info new file mode 100644 index 0000000000..4686c4a19d --- /dev/null +++ b/crypto/encode_decode/build.info @@ -0,0 +1,8 @@ +SOURCE[../../libcrypto]=endecode_pass.c + +SOURCE[../../libcrypto]=encoder_meth.c encoder_lib.c encoder_pkey.c +SOURCE[../../libcrypto]=decoder_meth.c decoder_lib.c \ + decoder_pkey.c + +SOURCE[../../libcrypto]=encoder_err.c +SOURCE[../../libcrypto]=decoder_err.c diff --git a/crypto/encode_decode/decoder_err.c b/crypto/encode_decode/decoder_err.c new file mode 100644 index 0000000000..984f7abeb9 --- /dev/null +++ b/crypto/encode_decode/decoder_err.c @@ -0,0 +1,31 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include + +#ifndef OPENSSL_NO_ERR + +static const ERR_STRING_DATA OSSL_DECODER_str_reasons[] = { + {ERR_PACK(ERR_LIB_OSSL_DECODER, 0, OSSL_DECODER_R_MISSING_GET_PARAMS), + "missing get params"}, + {0, NULL} +}; + +#endif + +int ERR_load_OSSL_DECODER_strings(void) +{ +#ifndef OPENSSL_NO_ERR + if (ERR_reason_error_string(OSSL_DECODER_str_reasons[0].error) == NULL) + ERR_load_strings_const(OSSL_DECODER_str_reasons); +#endif + return 1; +} diff --git a/crypto/encode_decode/decoder_lib.c b/crypto/encode_decode/decoder_lib.c new file mode 100644 index 0000000000..21b4703084 --- /dev/null +++ b/crypto/encode_decode/decoder_lib.c @@ -0,0 +1,483 @@ +/* + * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "encoder_local.h" +#include "e_os.h" + +struct decoder_process_data_st { + OSSL_DECODER_CTX *ctx; + + /* Current BIO */ + BIO *bio; + + /* Index of the current decoder instance to be processed */ + size_t current_deser_inst_index; +}; + +static int decoder_process(const OSSL_PARAM params[], void *arg); + +int OSSL_DECODER_from_bio(OSSL_DECODER_CTX *ctx, BIO *in) +{ + struct decoder_process_data_st data; + int ok = 0; + + memset(&data, 0, sizeof(data)); + data.ctx = ctx; + data.bio = in; + + ok = decoder_process(NULL, &data); + + /* Clear any internally cached passphrase */ + if (!ctx->flag_user_passphrase) { + OSSL_DECODER_CTX_set_passphrase(ctx, NULL, 0); + ctx->flag_user_passphrase = 0; + } + return ok; +} + +#ifndef OPENSSL_NO_STDIO +static BIO *bio_from_file(FILE *fp) +{ + BIO *b; + + if ((b = BIO_new(BIO_s_file())) == NULL) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_BIO_LIB); + return NULL; + } + BIO_set_fp(b, fp, BIO_NOCLOSE); + return b; +} + +int OSSL_DECODER_from_fp(OSSL_DECODER_CTX *ctx, FILE *fp) +{ + BIO *b = bio_from_file(fp); + int ret = 0; + + if (b != NULL) + ret = OSSL_DECODER_from_bio(ctx, b); + + BIO_free(b); + return ret; +} +#endif + +int OSSL_DECODER_CTX_set_input_type(OSSL_DECODER_CTX *ctx, + const char *input_type) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + /* + * NULL is a valid starting input type, and means that the caller leaves + * it to code to discover what the starting input type is. + */ + ctx->start_input_type = input_type; + return 1; +} + +int OSSL_DECODER_CTX_add_decoder(OSSL_DECODER_CTX *ctx, OSSL_DECODER *decoder) +{ + OSSL_DECODER_INSTANCE *decoder_inst = NULL; + const OSSL_PROVIDER *prov = NULL; + OSSL_PARAM params[2]; + void *provctx = NULL; + + if (!ossl_assert(ctx != NULL) || !ossl_assert(decoder != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (decoder->get_params == NULL) { + ERR_raise(ERR_LIB_OSSL_DECODER, + OSSL_DECODER_R_MISSING_GET_PARAMS); + return 0; + } + + if (ctx->decoder_insts == NULL + && (ctx->decoder_insts = + sk_OSSL_DECODER_INSTANCE_new_null()) == NULL) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE); + return 0; + } + if ((decoder_inst = OPENSSL_zalloc(sizeof(*decoder_inst))) == NULL) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE); + return 0; + } + if (!OSSL_DECODER_up_ref(decoder)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_INTERNAL_ERROR); + goto err; + } + decoder_inst->decoder = decoder; + + prov = OSSL_DECODER_provider(decoder_inst->decoder); + provctx = OSSL_PROVIDER_get0_provider_ctx(prov); + + /* Cache the input type for this encoder */ + params[0] = + OSSL_PARAM_construct_utf8_ptr(OSSL_DECODER_PARAM_INPUT_TYPE, + (char **)&decoder_inst->input_type, 0); + params[1] = OSSL_PARAM_construct_end(); + + if (!decoder_inst->decoder->get_params(params) + || !OSSL_PARAM_modified(¶ms[0])) + goto err; + + if ((decoder_inst->deserctx = decoder_inst->decoder->newctx(provctx)) + == NULL) + goto err; + + if (sk_OSSL_DECODER_INSTANCE_push(ctx->decoder_insts, decoder_inst) <= 0) + goto err; + + return 1; + err: + if (decoder_inst != NULL) { + if (decoder_inst->decoder != NULL) + decoder_inst->decoder->freectx(decoder_inst->deserctx); + OSSL_DECODER_free(decoder_inst->decoder); + OPENSSL_free(decoder_inst); + } + return 0; +} + +int OSSL_DECODER_CTX_add_extra(OSSL_DECODER_CTX *ctx, + OPENSSL_CTX *libctx, const char *propq) +{ + /* + * This function goes through existing decoder methods in + * |ctx->decoder_insts|, and tries to fetch new decoders that produce + * what the existing ones want as input, and push those newly fetched + * decoders on top of the same stack. + * Then it does the same again, but looping over the newly fetched + * decoders, until there are no more encoders to be fetched, or + * when we have done this 10 times. + * + * we do this with sliding windows on the stack by keeping track of indexes + * and of the end. + * + * +----------------+ + * | DER to RSA | <--- w_prev_start + * +----------------+ + * | DER to DSA | + * +----------------+ + * | DER to DH | + * +----------------+ + * | PEM to DER | <--- w_prev_end, w_new_start + * +----------------+ + * <--- w_new_end + */ + size_t w_prev_start, w_prev_end; /* "previous" decoders */ + size_t w_new_start, w_new_end; /* "new" decoders */ + size_t count = 0; /* Calculates how many were added in each iteration */ + size_t depth = 0; /* Counts the number of iterations */ + + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + /* + * If there is no stack of OSSL_DECODER_INSTANCE, we have nothing + * more to add. That's fine. + */ + if (ctx->decoder_insts == NULL) + return 1; + + w_prev_start = 0; + w_prev_end = sk_OSSL_DECODER_INSTANCE_num(ctx->decoder_insts); + do { + size_t i; + + w_new_start = w_new_end = w_prev_end; + + for (i = w_prev_start; i < w_prev_end; i++) { + OSSL_DECODER_INSTANCE *decoder_inst = + sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, i); + const char *name = decoder_inst->input_type; + OSSL_DECODER *decoder = NULL; + + /* + * If the caller has specified what the initial input should be, + * and the decoder implementation we're looking at has that + * input type, there's no point adding on more implementations + * on top of this one, so we don't. + */ + if (ctx->start_input_type != NULL + && strcasecmp(ctx->start_input_type, + decoder_inst->input_type) != 0) + continue; + + ERR_set_mark(); + decoder = OSSL_DECODER_fetch(libctx, name, propq); + ERR_pop_to_mark(); + + if (decoder != NULL) { + size_t j; + + /* + * Check that we don't already have this decoder in our + * stack We only need to check among the newly added ones. + */ + for (j = w_new_start; j < w_new_end; j++) { + OSSL_DECODER_INSTANCE *check_inst = + sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, j); + + if (decoder == check_inst->decoder) { + /* We found it, so drop the new fetch */ + OSSL_DECODER_free(decoder); + decoder = NULL; + break; + } + } + } + + if (decoder == NULL) + continue; + + /* + * Apart from keeping w_new_end up to date, We don't care about + * errors here. If it doesn't collect, then it doesn't... + */ + if (OSSL_DECODER_CTX_add_decoder(ctx, decoder)) /* ref++ */ + w_new_end++; + OSSL_DECODER_free(decoder); /* ref-- */ + } + /* How many were added in this iteration */ + count = w_new_end - w_new_start; + + /* Slide the "previous decoder" windows */ + w_prev_start = w_new_start; + w_prev_end = w_new_end; + + depth++; + } while (count != 0 && depth <= 10); + + return 1; +} + +int OSSL_DECODER_CTX_num_decoders(OSSL_DECODER_CTX *ctx) +{ + if (ctx == NULL || ctx->decoder_insts == NULL) + return 0; + return sk_OSSL_DECODER_INSTANCE_num(ctx->decoder_insts); +} + +int OSSL_DECODER_CTX_set_construct(OSSL_DECODER_CTX *ctx, + OSSL_DECODER_CONSTRUCT *construct) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + ctx->construct = construct; + return 1; +} + +int OSSL_DECODER_CTX_set_construct_data(OSSL_DECODER_CTX *ctx, + void *construct_data) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + ctx->construct_data = construct_data; + return 1; +} + +int OSSL_DECODER_CTX_set_cleanup(OSSL_DECODER_CTX *ctx, + OSSL_DECODER_CLEANUP *cleanup) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + ctx->cleanup = cleanup; + return 1; +} + +OSSL_DECODER_CONSTRUCT * +OSSL_DECODER_CTX_get_construct(OSSL_DECODER_CTX *ctx) +{ + if (ctx == NULL) + return NULL; + return ctx->construct; +} + +void *OSSL_DECODER_CTX_get_construct_data(OSSL_DECODER_CTX *ctx) +{ + if (ctx == NULL) + return NULL; + return ctx->construct_data; +} + +OSSL_DECODER_CLEANUP * +OSSL_DECODER_CTX_get_cleanup(OSSL_DECODER_CTX *ctx) +{ + if (ctx == NULL) + return NULL; + return ctx->cleanup; +} + +int OSSL_DECODER_export(OSSL_DECODER_INSTANCE *decoder_inst, + void *reference, size_t reference_sz, + OSSL_CALLBACK *export_cb, void *export_cbarg) +{ + if (!(ossl_assert(decoder_inst != NULL) + && ossl_assert(reference != NULL) + && ossl_assert(export_cb != NULL) + && ossl_assert(export_cbarg != NULL))) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return decoder_inst->decoder->export_object(decoder_inst->deserctx, + reference, reference_sz, + export_cb, export_cbarg); +} + +OSSL_DECODER *OSSL_DECODER_INSTANCE_decoder(OSSL_DECODER_INSTANCE *decoder_inst) +{ + if (decoder_inst == NULL) + return NULL; + return decoder_inst->decoder; +} + +void *OSSL_DECODER_INSTANCE_decoder_ctx(OSSL_DECODER_INSTANCE *decoder_inst) +{ + if (decoder_inst == NULL) + return NULL; + return decoder_inst->deserctx; +} + +static int decoder_process(const OSSL_PARAM params[], void *arg) +{ + struct decoder_process_data_st *data = arg; + OSSL_DECODER_CTX *ctx = data->ctx; + OSSL_DECODER_INSTANCE *decoder_inst = NULL; + OSSL_DECODER *decoder = NULL; + BIO *bio = data->bio; + long loc; + size_t i; + int ok = 0; + /* For recursions */ + struct decoder_process_data_st new_data; + + memset(&new_data, 0, sizeof(new_data)); + new_data.ctx = data->ctx; + + if (params == NULL) { + /* First iteration, where we prepare for what is to come */ + + data->current_deser_inst_index = + OSSL_DECODER_CTX_num_decoders(ctx); + + bio = data->bio; + } else { + const OSSL_PARAM *p; + + decoder_inst = + sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, + data->current_deser_inst_index); + decoder = OSSL_DECODER_INSTANCE_decoder(decoder_inst); + + if (ctx->construct != NULL + && ctx->construct(decoder_inst, params, ctx->construct_data)) { + ok = 1; + goto end; + } + + /* The constructor didn't return success */ + + /* + * so we try to use the object we got and feed it to any next + * decoder that will take it. Object references are not + * allowed for this. + * If this data isn't present, decoding has failed. + */ + + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_DATA); + if (p == NULL || p->data_type != OSSL_PARAM_OCTET_STRING) + goto end; + new_data.bio = BIO_new_mem_buf(p->data, (int)p->data_size); + if (new_data.bio == NULL) + goto end; + bio = new_data.bio; + } + + /* + * If we have no more decoders to look through at this point, + * we failed + */ + if (data->current_deser_inst_index == 0) + goto end; + + if ((loc = BIO_tell(bio)) < 0) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_BIO_LIB); + goto end; + } + + for (i = data->current_deser_inst_index; i-- > 0;) { + OSSL_DECODER_INSTANCE *new_deser_inst = + sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, i); + OSSL_DECODER *new_deser = + OSSL_DECODER_INSTANCE_decoder(new_deser_inst); + + /* + * If |decoder| is NULL, it means we've just started, and the caller + * may have specified what it expects the initial input to be. If + * that's the case, we do this extra check. + */ + if (decoder == NULL && ctx->start_input_type != NULL + && strcasecmp(ctx->start_input_type, + new_deser_inst->input_type) != 0) + continue; + + /* + * If we have a previous decoder, we check that the input type + * of the next to be used matches the type of this previous one. + * decoder_inst->input_type is a cache of the parameter "input-type" + * value for that decoder. + */ + if (decoder != NULL + && !OSSL_DECODER_is_a(decoder, new_deser_inst->input_type)) + continue; + + /* + * Checking the return value of BIO_reset() or BIO_seek() is unsafe. + * Furthermore, BIO_reset() is unsafe to use if the source BIO happens + * to be a BIO_s_mem(), because the earlier BIO_tell() gives us zero + * no matter where we are in the underlying buffer we're reading from. + * + * So, we simply do a BIO_seek(), and use BIO_tell() that we're back + * at the same position. This is a best effort attempt, but BIO_seek() + * and BIO_tell() should come as a pair... + */ + (void)BIO_seek(bio, loc); + if (BIO_tell(bio) != loc) + goto end; + + /* Recurse */ + new_data.current_deser_inst_index = i; + ok = new_deser->decode(new_deser_inst->deserctx, (OSSL_CORE_BIO *)bio, + decoder_process, &new_data, + ctx->passphrase_cb, new_data.ctx); + if (ok) + break; + } + + end: + BIO_free(new_data.bio); + return ok; +} diff --git a/crypto/encode_decode/decoder_meth.c b/crypto/encode_decode/decoder_meth.c new file mode 100644 index 0000000000..2259c6348a --- /dev/null +++ b/crypto/encode_decode/decoder_meth.c @@ -0,0 +1,552 @@ +/* + * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "internal/core.h" +#include "internal/namemap.h" +#include "internal/property.h" +#include "internal/provider.h" +#include "crypto/encoder.h" +#include "encoder_local.h" + +static void OSSL_DECODER_INSTANCE_free(OSSL_DECODER_INSTANCE *instance); + +/* + * Decoder can have multiple names, separated with colons in a name string + */ +#define NAME_SEPARATOR ':' + +/* Simple method structure constructor and destructor */ +static OSSL_DECODER *ossl_decoder_new(void) +{ + OSSL_DECODER *decoder = NULL; + + if ((decoder = OPENSSL_zalloc(sizeof(*decoder))) == NULL + || (decoder->base.lock = CRYPTO_THREAD_lock_new()) == NULL) { + OSSL_DECODER_free(decoder); + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE); + return NULL; + } + + decoder->base.refcnt = 1; + + return decoder; +} + +int OSSL_DECODER_up_ref(OSSL_DECODER *decoder) +{ + int ref = 0; + + CRYPTO_UP_REF(&decoder->base.refcnt, &ref, decoder->base.lock); + return 1; +} + +void OSSL_DECODER_free(OSSL_DECODER *decoder) +{ + int ref = 0; + + if (decoder == NULL) + return; + + CRYPTO_DOWN_REF(&decoder->base.refcnt, &ref, decoder->base.lock); + if (ref > 0) + return; + ossl_provider_free(decoder->base.prov); + CRYPTO_THREAD_lock_free(decoder->base.lock); + OPENSSL_free(decoder); +} + +/* Permanent decoder method store, constructor and destructor */ +static void decoder_store_free(void *vstore) +{ + ossl_method_store_free(vstore); +} + +static void *decoder_store_new(OPENSSL_CTX *ctx) +{ + return ossl_method_store_new(ctx); +} + + +static const OPENSSL_CTX_METHOD decoder_store_method = { + decoder_store_new, + decoder_store_free, +}; + +/* Data to be passed through ossl_method_construct() */ +struct decoder_data_st { + OPENSSL_CTX *libctx; + OSSL_METHOD_CONSTRUCT_METHOD *mcm; + int id; /* For get_decoder_from_store() */ + const char *names; /* For get_decoder_from_store() */ + const char *propquery; /* For get_decoder_from_store() */ +}; + +/* + * Generic routines to fetch / create DECODER methods with + * ossl_method_construct() + */ + +/* Temporary decoder method store, constructor and destructor */ +static void *alloc_tmp_decoder_store(OPENSSL_CTX *ctx) +{ + return ossl_method_store_new(ctx); +} + +static void dealloc_tmp_decoder_store(void *store) +{ + if (store != NULL) + ossl_method_store_free(store); +} + +/* Get the permanent decoder store */ +static OSSL_METHOD_STORE *get_decoder_store(OPENSSL_CTX *libctx) +{ + return openssl_ctx_get_data(libctx, OPENSSL_CTX_DECODER_STORE_INDEX, + &decoder_store_method); +} + +/* Get decoder methods from a store, or put one in */ +static void *get_decoder_from_store(OPENSSL_CTX *libctx, void *store, + void *data) +{ + struct decoder_data_st *methdata = data; + void *method = NULL; + int id; + + if ((id = methdata->id) == 0) { + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + + id = ossl_namemap_name2num(namemap, methdata->names); + } + + if (store == NULL + && (store = get_decoder_store(libctx)) == NULL) + return NULL; + + if (!ossl_method_store_fetch(store, id, methdata->propquery, &method)) + return NULL; + return method; +} + +static int put_decoder_in_store(OPENSSL_CTX *libctx, void *store, + void *method, const OSSL_PROVIDER *prov, + int operation_id, const char *names, + const char *propdef, void *unused) +{ + OSSL_NAMEMAP *namemap; + int id; + + if ((namemap = ossl_namemap_stored(libctx)) == NULL + || (id = ossl_namemap_name2num(namemap, names)) == 0) + return 0; + + if (store == NULL && (store = get_decoder_store(libctx)) == NULL) + return 0; + + return ossl_method_store_add(store, prov, id, propdef, method, + (int (*)(void *))OSSL_DECODER_up_ref, + (void (*)(void *))OSSL_DECODER_free); +} + +/* Create and populate a decoder method */ +static void *decoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov) +{ + OSSL_DECODER *decoder = NULL; + const OSSL_DISPATCH *fns = algodef->implementation; + + if ((decoder = ossl_decoder_new()) == NULL) + return NULL; + decoder->base.id = id; + decoder->base.propdef = algodef->property_definition; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_FUNC_DECODER_NEWCTX: + if (decoder->newctx == NULL) + decoder->newctx = OSSL_FUNC_decoder_newctx(fns); + break; + case OSSL_FUNC_DECODER_FREECTX: + if (decoder->freectx == NULL) + decoder->freectx = OSSL_FUNC_decoder_freectx(fns); + break; + case OSSL_FUNC_DECODER_GET_PARAMS: + if (decoder->get_params == NULL) + decoder->get_params = + OSSL_FUNC_decoder_get_params(fns); + break; + case OSSL_FUNC_DECODER_GETTABLE_PARAMS: + if (decoder->gettable_params == NULL) + decoder->gettable_params = + OSSL_FUNC_decoder_gettable_params(fns); + break; + case OSSL_FUNC_DECODER_SET_CTX_PARAMS: + if (decoder->set_ctx_params == NULL) + decoder->set_ctx_params = + OSSL_FUNC_decoder_set_ctx_params(fns); + break; + case OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS: + if (decoder->settable_ctx_params == NULL) + decoder->settable_ctx_params = + OSSL_FUNC_decoder_settable_ctx_params(fns); + break; + case OSSL_FUNC_DECODER_DECODE: + if (decoder->decode == NULL) + decoder->decode = OSSL_FUNC_decoder_decode(fns); + break; + case OSSL_FUNC_DECODER_EXPORT_OBJECT: + if (decoder->export_object == NULL) + decoder->export_object = OSSL_FUNC_decoder_export_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. + */ + if (!((decoder->newctx == NULL && decoder->freectx == NULL) + || (decoder->newctx != NULL && decoder->freectx != NULL)) + || (decoder->decode == NULL && decoder->export_object == NULL)) { + OSSL_DECODER_free(decoder); + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_INVALID_PROVIDER_FUNCTIONS); + return NULL; + } + + if (prov != NULL && !ossl_provider_up_ref(prov)) { + OSSL_DECODER_free(decoder); + return NULL; + } + + decoder->base.prov = prov; + return decoder; +} + + +/* + * The core fetching functionality passes the names of the implementation. + * This function is responsible to getting an identity number for them, + * then call decoder_from_dispatch() with that identity number. + */ +static void *construct_decoder(const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov, void *unused) +{ + /* + * This function is only called if get_decoder_from_store() returned + * NULL, so it's safe to say that of all the spots to create a new + * namemap entry, this is it. Should the name already exist there, we + * know that ossl_namemap_add() will return its corresponding number. + */ + OPENSSL_CTX *libctx = ossl_provider_library_context(prov); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + const char *names = algodef->algorithm_names; + int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR); + void *method = NULL; + + if (id != 0) + method = decoder_from_dispatch(id, algodef, prov); + + return method; +} + +/* Intermediary function to avoid ugly casts, used below */ +static void destruct_decoder(void *method, void *data) +{ + OSSL_DECODER_free(method); +} + +static int up_ref_decoder(void *method) +{ + return OSSL_DECODER_up_ref(method); +} + +static void free_decoder(void *method) +{ + OSSL_DECODER_free(method); +} + +/* Fetching support. Can fetch by numeric identity or by name */ +static OSSL_DECODER *inner_ossl_decoder_fetch(OPENSSL_CTX *libctx, int id, + const char *name, + const char *properties) +{ + OSSL_METHOD_STORE *store = get_decoder_store(libctx); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + void *method = NULL; + + if (store == NULL || namemap == NULL) + return NULL; + + /* + * If we have been passed neither a name_id or a name, we have an + * internal programming error. + */ + if (!ossl_assert(id != 0 || name != NULL)) + return NULL; + + if (id == 0) + id = ossl_namemap_name2num(namemap, name); + + if (id == 0 + || !ossl_method_store_cache_get(store, id, properties, &method)) { + OSSL_METHOD_CONSTRUCT_METHOD mcm = { + alloc_tmp_decoder_store, + dealloc_tmp_decoder_store, + get_decoder_from_store, + put_decoder_in_store, + construct_decoder, + destruct_decoder + }; + struct decoder_data_st mcmdata; + + mcmdata.libctx = libctx; + mcmdata.mcm = &mcm; + mcmdata.id = id; + mcmdata.names = name; + mcmdata.propquery = properties; + if ((method = ossl_method_construct(libctx, OSSL_OP_DECODER, + 0 /* !force_cache */, + &mcm, &mcmdata)) != NULL) { + /* + * If construction did create a method for us, we know that + * there is a correct name_id and meth_id, since those have + * already been calculated in get_decoder_from_store() and + * put_decoder_in_store() above. + */ + if (id == 0) + id = ossl_namemap_name2num(namemap, name); + ossl_method_store_cache_set(store, id, properties, method, + up_ref_decoder, free_decoder); + } + } + + return method; +} + +OSSL_DECODER *OSSL_DECODER_fetch(OPENSSL_CTX *libctx, const char *name, + const char *properties) +{ + return inner_ossl_decoder_fetch(libctx, 0, name, properties); +} + +OSSL_DECODER *ossl_decoder_fetch_by_number(OPENSSL_CTX *libctx, int id, + const char *properties) +{ + return inner_ossl_decoder_fetch(libctx, id, NULL, properties); +} + +/* + * Library of basic method functions + */ + +const OSSL_PROVIDER *OSSL_DECODER_provider(const OSSL_DECODER *decoder) +{ + if (!ossl_assert(decoder != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return decoder->base.prov; +} + +const char *OSSL_DECODER_properties(const OSSL_DECODER *decoder) +{ + if (!ossl_assert(decoder != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return decoder->base.propdef; +} + +int OSSL_DECODER_number(const OSSL_DECODER *decoder) +{ + if (!ossl_assert(decoder != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return decoder->base.id; +} + +int OSSL_DECODER_is_a(const OSSL_DECODER *decoder, const char *name) +{ + if (decoder->base.prov != NULL) { + OPENSSL_CTX *libctx = ossl_provider_library_context(decoder->base.prov); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + + return ossl_namemap_name2num(namemap, name) == decoder->base.id; + } + return 0; +} + +struct decoder_do_all_data_st { + void (*user_fn)(void *method, void *arg); + void *user_arg; +}; + +static void decoder_do_one(OSSL_PROVIDER *provider, + const OSSL_ALGORITHM *algodef, + int no_store, void *vdata) +{ + struct decoder_do_all_data_st *data = vdata; + OPENSSL_CTX *libctx = ossl_provider_library_context(provider); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + const char *names = algodef->algorithm_names; + int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR); + void *method = NULL; + + if (id != 0) + method = + decoder_from_dispatch(id, algodef, provider); + + if (method != NULL) { + data->user_fn(method, data->user_arg); + OSSL_DECODER_free(method); + } +} + +void OSSL_DECODER_do_all_provided(OPENSSL_CTX *libctx, + void (*fn)(OSSL_DECODER *decoder, void *arg), + void *arg) +{ + struct decoder_do_all_data_st data; + + data.user_fn = (void (*)(void *, void *))fn; + data.user_arg = arg; + ossl_algorithm_do_all(libctx, OSSL_OP_DECODER, NULL, + NULL, decoder_do_one, NULL, + &data); +} + +void OSSL_DECODER_names_do_all(const OSSL_DECODER *decoder, + void (*fn)(const char *name, void *data), + void *data) +{ + if (decoder == NULL) + return; + + if (decoder->base.prov != NULL) { + OPENSSL_CTX *libctx = ossl_provider_library_context(decoder->base.prov); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + + ossl_namemap_doall_names(namemap, decoder->base.id, fn, data); + } +} + +const OSSL_PARAM * +OSSL_DECODER_gettable_params(OSSL_DECODER *decoder) +{ + if (decoder != NULL && decoder->gettable_params != NULL) { + void *provctx = ossl_provider_ctx(OSSL_DECODER_provider(decoder)); + + return decoder->gettable_params(provctx); + } + return NULL; +} + +int OSSL_DECODER_get_params(OSSL_DECODER *decoder, OSSL_PARAM params[]) +{ + if (decoder != NULL && decoder->get_params != NULL) + return decoder->get_params(params); + return 0; +} + +const OSSL_PARAM * +OSSL_DECODER_settable_ctx_params(OSSL_DECODER *decoder) +{ + if (decoder != NULL && decoder->settable_ctx_params != NULL) { + void *provctx = ossl_provider_ctx(OSSL_DECODER_provider(decoder)); + + return decoder->settable_ctx_params(provctx); + } + return NULL; +} + +/* + * Decoder context support + */ + +/* + * |encoder| value NULL is valid, and signifies that there is no decoder. + * This is useful to provide fallback mechanisms. + * Functions that want to verify if there is a decoder can do so with + * OSSL_DECODER_CTX_get_decoder() + */ +OSSL_DECODER_CTX *OSSL_DECODER_CTX_new(void) +{ + OSSL_DECODER_CTX *ctx; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE); + return NULL; + } + + ctx->passphrase_cb = ossl_decoder_passphrase_in_cb; + return ctx; +} + +int OSSL_DECODER_CTX_set_params(OSSL_DECODER_CTX *ctx, + const OSSL_PARAM params[]) +{ + size_t i; + size_t l; + + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (ctx->decoder_insts == NULL) + return 1; + + l = (size_t)sk_OSSL_DECODER_INSTANCE_num(ctx->decoder_insts); + for (i = 0; i < l; i++) { + OSSL_DECODER_INSTANCE *decoder_inst = + sk_OSSL_DECODER_INSTANCE_value(ctx->decoder_insts, i); + + if (decoder_inst->deserctx == NULL + || decoder_inst->decoder->set_ctx_params == NULL) + continue; + if (!decoder_inst->decoder->set_ctx_params(decoder_inst->deserctx, params)) + return 0; + } + return 1; +} + +static void +OSSL_DECODER_INSTANCE_free(OSSL_DECODER_INSTANCE *decoder_inst) +{ + if (decoder_inst != NULL) { + if (decoder_inst->decoder->freectx != NULL) + decoder_inst->decoder->freectx(decoder_inst->deserctx); + decoder_inst->deserctx = NULL; + OSSL_DECODER_free(decoder_inst->decoder); + decoder_inst->decoder = NULL; + OPENSSL_free(decoder_inst); + decoder_inst = NULL; + } +} + +void OSSL_DECODER_CTX_free(OSSL_DECODER_CTX *ctx) +{ + if (ctx != NULL) { + if (ctx->cleanup != NULL) + ctx->cleanup(ctx->construct_data); + sk_OSSL_DECODER_INSTANCE_pop_free(ctx->decoder_insts, + OSSL_DECODER_INSTANCE_free); + OSSL_DECODER_CTX_set_passphrase_ui(ctx, NULL, NULL); + OSSL_DECODER_CTX_set_passphrase(ctx, NULL, 0); + OPENSSL_free(ctx); + } +} diff --git a/crypto/encode_decode/decoder_pkey.c b/crypto/encode_decode/decoder_pkey.c new file mode 100644 index 0000000000..7f468c2476 --- /dev/null +++ b/crypto/encode_decode/decoder_pkey.c @@ -0,0 +1,374 @@ +/* + * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include "crypto/evp.h" +#include "encoder_local.h" + +int OSSL_DECODER_CTX_set_passphrase(OSSL_DECODER_CTX *ctx, + const unsigned char *kstr, + size_t klen) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + OPENSSL_clear_free(ctx->cached_passphrase, ctx->cached_passphrase_len); + ctx->cached_passphrase = NULL; + ctx->cached_passphrase_len = 0; + if (kstr != NULL) { + if (klen == 0) { + ctx->cached_passphrase = OPENSSL_zalloc(1); + ctx->cached_passphrase_len = 0; + } else { + ctx->cached_passphrase = OPENSSL_memdup(kstr, klen); + ctx->cached_passphrase_len = klen; + } + if (ctx->cached_passphrase == NULL) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE); + return 0; + } + } + ctx->flag_user_passphrase = 1; + return 1; +} + +static void decoder_ctx_reset_passphrase_ui(OSSL_DECODER_CTX *ctx) +{ + UI_destroy_method(ctx->allocated_ui_method); + ctx->allocated_ui_method = NULL; + ctx->ui_method = NULL; + ctx->ui_data = NULL; +} + +int OSSL_DECODER_CTX_set_passphrase_ui(OSSL_DECODER_CTX *ctx, + const UI_METHOD *ui_method, + void *ui_data) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + decoder_ctx_reset_passphrase_ui(ctx); + ctx->ui_method = ui_method; + ctx->ui_data = ui_data; + return 1; +} + +int OSSL_DECODER_CTX_set_pem_password_cb(OSSL_DECODER_CTX *ctx, + pem_password_cb *cb, void *cbarg) +{ + UI_METHOD *ui_method = NULL; + + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + /* + * If |cb| is NULL, it means the caller wants to reset previous + * password callback info. Otherwise, we only set the new data + * if a new UI_METHOD could be created for this sort of callback. + */ + if (cb == NULL + || (ui_method = UI_UTIL_wrap_read_pem_callback(cb, 0)) != NULL) { + decoder_ctx_reset_passphrase_ui(ctx); + ctx->ui_method = ctx->allocated_ui_method = ui_method; + ctx->ui_data = cbarg; + ctx->passphrase_cb = ossl_decoder_passphrase_in_cb; + return 1; + } + + return 0; +} + +/* + * Support for OSSL_DECODER_CTX_new_by_EVP_PKEY: + * The construct data, and collecting keymgmt information for it + */ + +DEFINE_STACK_OF(EVP_KEYMGMT) + +struct decoder_EVP_PKEY_data_st { + char *object_type; /* recorded object data type, may be NULL */ + void **object; /* Where the result should end up */ + STACK_OF(EVP_KEYMGMT) *keymgmts; /* The EVP_KEYMGMTs we handle */ +}; + +static int decoder_construct_EVP_PKEY(OSSL_DECODER_INSTANCE *decoder_inst, + const OSSL_PARAM *params, + void *construct_data) +{ + struct decoder_EVP_PKEY_data_st *data = construct_data; + OSSL_DECODER *decoder = + OSSL_DECODER_INSTANCE_decoder(decoder_inst); + void *deserctx = OSSL_DECODER_INSTANCE_decoder_ctx(decoder_inst); + size_t i, end_i; + /* + * |object_ref| points to a provider reference to an object, its exact + * contents entirely opaque to us, but may be passed to any provider + * function that expects this (such as OSSL_FUNC_keymgmt_load(). + * + * This pointer is considered volatile, i.e. whatever it points at + * is assumed to be freed as soon as this function returns. + */ + void *object_ref = NULL; + size_t object_ref_sz = 0; + const OSSL_PARAM *p; + + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_DATA_TYPE); + if (p != NULL) { + char *object_type = NULL; + + if (!OSSL_PARAM_get_utf8_string(p, &object_type, 0)) + return 0; + OPENSSL_free(data->object_type); + data->object_type = object_type; + } + + /* + * For stuff that should end up in an EVP_PKEY, we only accept an object + * reference for the moment. This enforces that the key data itself + * remains with the provider. + */ + p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_REFERENCE); + if (p == NULL || p->data_type != OSSL_PARAM_OCTET_STRING) + return 0; + object_ref = p->data; + object_ref_sz = p->data_size; + + /* We may have reached one of the goals, let's find out! */ + end_i = sk_EVP_KEYMGMT_num(data->keymgmts); + for (i = 0; end_i; i++) { + EVP_KEYMGMT *keymgmt = sk_EVP_KEYMGMT_value(data->keymgmts, i); + + /* + * There are two ways to find a matching KEYMGMT: + * + * 1. If the object data type (recorded in |data->object_type|) + * is defined, by checking it using EVP_KEYMGMT_is_a(). + * 2. If the object data type is NOT defined, by comparing the + * EVP_KEYMGMT and OSSL_DECODER method numbers. Since + * EVP_KEYMGMT and OSSL_DECODE operate with the same + * namemap, we know that the method numbers must match. + * + * This allows individual decoders to specify variants of keys, + * such as a DER to RSA decoder finding a RSA-PSS key, without + * having to decode the exact same DER blob into the exact same + * internal structure twice. This is, of course, entirely at the + * discretion of the decoder implementations. + */ + if (data->object_type != NULL + ? EVP_KEYMGMT_is_a(keymgmt, data->object_type) + : EVP_KEYMGMT_number(keymgmt) == OSSL_DECODER_number(decoder)) { + EVP_PKEY *pkey = NULL; + void *keydata = NULL; + const OSSL_PROVIDER *keymgmt_prov = + EVP_KEYMGMT_provider(keymgmt); + const OSSL_PROVIDER *decoder_prov = + OSSL_DECODER_provider(decoder); + + /* + * If the EVP_KEYMGMT and the OSSL_DECODER are from the + * same provider, we assume that the KEYMGMT has a key loading + * function that can handle the provider reference we hold. + * + * Otherwise, we export from the decoder and import the + * result in the keymgmt. + */ + if (keymgmt_prov == decoder_prov) { + keydata = evp_keymgmt_load(keymgmt, object_ref, object_ref_sz); + } else { + struct evp_keymgmt_util_try_import_data_st import_data; + + import_data.keymgmt = keymgmt; + import_data.keydata = NULL; + import_data.selection = OSSL_KEYMGMT_SELECT_ALL; + + /* + * No need to check for errors here, the value of + * |import_data.keydata| is as much an indicator. + */ + (void)decoder->export_object(deserctx, object_ref, object_ref_sz, + &evp_keymgmt_util_try_import, + &import_data); + keydata = import_data.keydata; + import_data.keydata = NULL; + } + + if (keydata != NULL + && (pkey = + evp_keymgmt_util_make_pkey(keymgmt, keydata)) == NULL) + evp_keymgmt_freedata(keymgmt, keydata); + + *data->object = pkey; + + break; + } + } + /* + * We successfully looked through, |*ctx->object| determines if we + * actually found something. + */ + return (*data->object != NULL); +} + +static void decoder_clean_EVP_PKEY_construct_arg(void *construct_data) +{ + struct decoder_EVP_PKEY_data_st *data = construct_data; + + if (data != NULL) { + sk_EVP_KEYMGMT_pop_free(data->keymgmts, EVP_KEYMGMT_free); + OPENSSL_free(data->object_type); + OPENSSL_free(data); + } +} + +DEFINE_STACK_OF_CSTRING() + +struct collected_data_st { + struct decoder_EVP_PKEY_data_st *process_data; + STACK_OF(OPENSSL_CSTRING) *names; + OSSL_DECODER_CTX *ctx; + + unsigned int error_occured:1; +}; + +static void collect_keymgmt(EVP_KEYMGMT *keymgmt, void *arg) +{ + struct collected_data_st *data = arg; + + if (data->error_occured) + return; + + data->error_occured = 1; /* Assume the worst */ + + if (!EVP_KEYMGMT_up_ref(keymgmt) /* ref++ */) + return; + if (sk_EVP_KEYMGMT_push(data->process_data->keymgmts, keymgmt) <= 0) { + EVP_KEYMGMT_free(keymgmt); /* ref-- */ + return; + } + + data->error_occured = 0; /* All is good now */ +} + +static void collect_name(const char *name, void *arg) +{ + struct collected_data_st *data = arg; + + if (data->error_occured) + return; + + data->error_occured = 1; /* Assume the worst */ + + if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0) + return; + + data->error_occured = 0; /* All is good now */ +} + +static void collect_decoder(OSSL_DECODER *decoder, void *arg) +{ + struct collected_data_st *data = arg; + size_t i, end_i; + + if (data->error_occured) + 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); + + if (!OSSL_DECODER_is_a(decoder, name)) + continue; + (void)OSSL_DECODER_CTX_add_decoder(data->ctx, decoder); + } + + data->error_occured = 0; /* All is good now */ +} + +OSSL_DECODER_CTX *OSSL_DECODER_CTX_new_by_EVP_PKEY(EVP_PKEY **pkey, + const char *input_type, + OPENSSL_CTX *libctx, + const char *propquery) +{ + OSSL_DECODER_CTX *ctx = NULL; + struct collected_data_st *data = NULL; + size_t i, end_i; + + if ((ctx = OSSL_DECODER_CTX_new()) == NULL + || (data = OPENSSL_zalloc(sizeof(*data))) == NULL + || (data->process_data = + OPENSSL_zalloc(sizeof(*data->process_data))) == NULL + || (data->process_data->keymgmts + = sk_EVP_KEYMGMT_new_null()) == NULL + || (data->names = sk_OPENSSL_CSTRING_new_null()) == NULL) { + ERR_raise(ERR_LIB_OSSL_DECODER, ERR_R_MALLOC_FAILURE); + goto err; + } + data->process_data->object = (void **)pkey; + data->ctx = ctx; + OSSL_DECODER_CTX_set_input_type(ctx, input_type); + + /* First, find all keymgmts to form goals */ + EVP_KEYMGMT_do_all_provided(libctx, collect_keymgmt, data); + + if (data->error_occured) + goto err; + + /* Then, we collect all the keymgmt names */ + end_i = sk_EVP_KEYMGMT_num(data->process_data->keymgmts); + for (i = 0; i < end_i; i++) { + EVP_KEYMGMT *keymgmt = + sk_EVP_KEYMGMT_value(data->process_data->keymgmts, i); + + EVP_KEYMGMT_names_do_all(keymgmt, collect_name, data); + + if (data->error_occured) + goto err; + } + + /* + * Finally, find all decoders that have any keymgmt of the collected + * keymgmt names + */ + OSSL_DECODER_do_all_provided(libctx, collect_decoder, data); + + if (data->error_occured) + goto err; + + /* If we found no decoders to match the keymgmts, we err */ + if (OSSL_DECODER_CTX_num_decoders(ctx) == 0) + goto err; + + /* Finally, collect extra decoders based on what we already have */ + (void)OSSL_DECODER_CTX_add_extra(ctx, libctx, propquery); + + if (!OSSL_DECODER_CTX_set_construct(ctx, decoder_construct_EVP_PKEY) + || !OSSL_DECODER_CTX_set_construct_data(ctx, data->process_data) + || !OSSL_DECODER_CTX_set_cleanup(ctx, + decoder_clean_EVP_PKEY_construct_arg)) + goto err; + + data->process_data = NULL; + err: + decoder_clean_EVP_PKEY_construct_arg(data->process_data); + sk_OPENSSL_CSTRING_free(data->names); + OPENSSL_free(data); + return ctx; +} diff --git a/crypto/encode_decode/encoder_err.c b/crypto/encode_decode/encoder_err.c new file mode 100644 index 0000000000..5416f8390e --- /dev/null +++ b/crypto/encode_decode/encoder_err.c @@ -0,0 +1,33 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include + +#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"}, + {0, NULL} +}; + +#endif + +int ERR_load_OSSL_ENCODER_strings(void) +{ +#ifndef OPENSSL_NO_ERR + if (ERR_reason_error_string(OSSL_ENCODER_str_reasons[0].error) == NULL) + ERR_load_strings_const(OSSL_ENCODER_str_reasons); +#endif + return 1; +} diff --git a/crypto/encode_decode/encoder_lib.c b/crypto/encode_decode/encoder_lib.c new file mode 100644 index 0000000000..b083fa2d4c --- /dev/null +++ b/crypto/encode_decode/encoder_lib.c @@ -0,0 +1,43 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include "encoder_local.h" + +int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out) +{ + return ctx->do_output(ctx, out); +} + +#ifndef OPENSSL_NO_STDIO +static BIO *bio_from_file(FILE *fp) +{ + BIO *b; + + if ((b = BIO_new(BIO_s_file())) == NULL) { + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_BUF_LIB); + return NULL; + } + BIO_set_fp(b, fp, BIO_NOCLOSE); + return b; +} + +int OSSL_ENCODER_to_fp(OSSL_ENCODER_CTX *ctx, FILE *fp) +{ + BIO *b = bio_from_file(fp); + int ret = 0; + + if (b != NULL) + ret = OSSL_ENCODER_to_bio(ctx, b); + + BIO_free(b); + return ret; +} +#endif diff --git a/crypto/encode_decode/encoder_local.h b/crypto/encode_decode/encoder_local.h new file mode 100644 index 0000000000..34931d4e43 --- /dev/null +++ b/crypto/encode_decode/encoder_local.h @@ -0,0 +1,140 @@ +/* + * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include +#include "internal/cryptlib.h" +#include "internal/refcount.h" + +struct ossl_serdes_base_st { + OSSL_PROVIDER *prov; + int id; + const char *propdef; + + CRYPTO_REF_COUNT refcnt; + CRYPTO_RWLOCK *lock; +}; + +struct ossl_encoder_st { + struct ossl_serdes_base_st base; + OSSL_FUNC_encoder_newctx_fn *newctx; + OSSL_FUNC_encoder_freectx_fn *freectx; + 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; +}; + +struct ossl_decoder_st { + struct ossl_serdes_base_st base; + OSSL_FUNC_decoder_newctx_fn *newctx; + OSSL_FUNC_decoder_freectx_fn *freectx; + OSSL_FUNC_decoder_get_params_fn *get_params; + OSSL_FUNC_decoder_gettable_params_fn *gettable_params; + OSSL_FUNC_decoder_set_ctx_params_fn *set_ctx_params; + OSSL_FUNC_decoder_settable_ctx_params_fn *settable_ctx_params; + OSSL_FUNC_decoder_decode_fn *decode; + OSSL_FUNC_decoder_export_object_fn *export_object; +}; + +struct ossl_encoder_ctx_st { + OSSL_ENCODER *encoder; + void *serctx; + + 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|. + */ + const void *object; + int (*do_output)(OSSL_ENCODER_CTX *ctx, BIO *out); + + /* For any function that needs a passphrase reader */ + const UI_METHOD *ui_method; + void *ui_data; + /* + * if caller used OSSL_ENCODER_CTX_set_passphrase_cb(), we need + * intermediary storage. + */ + UI_METHOD *allocated_ui_method; +}; + +struct ossl_decoder_instance_st { + OSSL_DECODER *decoder; /* Never NULL */ + void *deserctx; /* Never NULL */ + const char *input_type; /* Never NULL */ +}; + +DEFINE_STACK_OF(OSSL_DECODER_INSTANCE) + +struct ossl_decoder_ctx_st { + /* + * The caller may know the input type of the data they pass. If not, + * this will remain NULL and the decoding functionality will start + * with trying to decode with any desencoder in |decoder_insts|, + * regardless of their respective input type. + */ + const char *start_input_type; + + /* + * Decoders that are components of any current decoding path. + */ + STACK_OF(OSSL_DECODER_INSTANCE) *decoder_insts; + + /* + * The constructors of a decoding, and its caller argument. + */ + OSSL_DECODER_CONSTRUCT *construct; + OSSL_DECODER_CLEANUP *cleanup; + void *construct_data; + + /* For any function that needs a passphrase reader */ + OSSL_PASSPHRASE_CALLBACK *passphrase_cb; + const UI_METHOD *ui_method; + void *ui_data; + /* + * if caller used OSSL_ENCODER_CTX_set_pem_password_cb(), we need + * intermediary storage. + */ + UI_METHOD *allocated_ui_method; + /* + * Because the same input may pass through more than one decoder, + * we cache any passphrase passed to us. The desrializing processor + * must clear this at the end of a run. + */ + unsigned char *cached_passphrase; + size_t cached_passphrase_len; + + /* + * Flag section. Keep these together + */ + + /* + * The passphrase was passed to us by the user. In that case, it + * should only be freed when freeing this context. + */ + unsigned int flag_user_passphrase:1; +}; + +/* Passphrase callbacks, found in serdes_pass.c */ + +/* + * Encoders typically want to get an outgoing passphrase, while + * decoders typically want to get en incoming passphrase. + */ +OSSL_PASSPHRASE_CALLBACK ossl_encoder_passphrase_out_cb; +OSSL_PASSPHRASE_CALLBACK ossl_decoder_passphrase_in_cb; diff --git a/crypto/encode_decode/encoder_meth.c b/crypto/encode_decode/encoder_meth.c new file mode 100644 index 0000000000..ef3c24433e --- /dev/null +++ b/crypto/encode_decode/encoder_meth.c @@ -0,0 +1,523 @@ +/* + * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "internal/core.h" +#include "internal/namemap.h" +#include "internal/property.h" +#include "internal/provider.h" +#include "crypto/encoder.h" +#include "encoder_local.h" + +/* + * Encoder can have multiple names, separated with colons in a name string + */ +#define NAME_SEPARATOR ':' + +/* Simple method structure constructor and destructor */ +static OSSL_ENCODER *ossl_encoder_new(void) +{ + OSSL_ENCODER *encoder = NULL; + + if ((encoder = OPENSSL_zalloc(sizeof(*encoder))) == NULL + || (encoder->base.lock = CRYPTO_THREAD_lock_new()) == NULL) { + OSSL_ENCODER_free(encoder); + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); + return NULL; + } + + encoder->base.refcnt = 1; + + return encoder; +} + +int OSSL_ENCODER_up_ref(OSSL_ENCODER *encoder) +{ + int ref = 0; + + CRYPTO_UP_REF(&encoder->base.refcnt, &ref, encoder->base.lock); + return 1; +} + +void OSSL_ENCODER_free(OSSL_ENCODER *encoder) +{ + int ref = 0; + + if (encoder == NULL) + return; + + CRYPTO_DOWN_REF(&encoder->base.refcnt, &ref, encoder->base.lock); + if (ref > 0) + return; + ossl_provider_free(encoder->base.prov); + CRYPTO_THREAD_lock_free(encoder->base.lock); + OPENSSL_free(encoder); +} + +/* Permanent encoder method store, constructor and destructor */ +static void encoder_store_free(void *vstore) +{ + ossl_method_store_free(vstore); +} + +static void *encoder_store_new(OPENSSL_CTX *ctx) +{ + return ossl_method_store_new(ctx); +} + + +static const OPENSSL_CTX_METHOD encoder_store_method = { + encoder_store_new, + encoder_store_free, +}; + +/* Data to be passed through ossl_method_construct() */ +struct encoder_data_st { + OPENSSL_CTX *libctx; + OSSL_METHOD_CONSTRUCT_METHOD *mcm; + int id; /* For get_encoder_from_store() */ + const char *names; /* For get_encoder_from_store() */ + const char *propquery; /* For get_encoder_from_store() */ +}; + +/* + * Generic routines to fetch / create ENCODER methods with + * ossl_method_construct() + */ + +/* Temporary encoder method store, constructor and destructor */ +static void *alloc_tmp_encoder_store(OPENSSL_CTX *ctx) +{ + return ossl_method_store_new(ctx); +} + +static void dealloc_tmp_encoder_store(void *store) +{ + if (store != NULL) + ossl_method_store_free(store); +} + +/* Get the permanent encoder store */ +static OSSL_METHOD_STORE *get_encoder_store(OPENSSL_CTX *libctx) +{ + return openssl_ctx_get_data(libctx, OPENSSL_CTX_ENCODER_STORE_INDEX, + &encoder_store_method); +} + +/* Get encoder methods from a store, or put one in */ +static void *get_encoder_from_store(OPENSSL_CTX *libctx, void *store, + void *data) +{ + struct encoder_data_st *methdata = data; + void *method = NULL; + int id; + + if ((id = methdata->id) == 0) { + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + + id = ossl_namemap_name2num(namemap, methdata->names); + } + + if (store == NULL + && (store = get_encoder_store(libctx)) == NULL) + return NULL; + + if (!ossl_method_store_fetch(store, id, methdata->propquery, &method)) + return NULL; + return method; +} + +static int put_encoder_in_store(OPENSSL_CTX *libctx, void *store, + void *method, const OSSL_PROVIDER *prov, + int operation_id, const char *names, + const char *propdef, void *unused) +{ + OSSL_NAMEMAP *namemap; + int id; + + if ((namemap = ossl_namemap_stored(libctx)) == NULL + || (id = ossl_namemap_name2num(namemap, names)) == 0) + return 0; + + if (store == NULL && (store = get_encoder_store(libctx)) == NULL) + return 0; + + return ossl_method_store_add(store, prov, id, propdef, method, + (int (*)(void *))OSSL_ENCODER_up_ref, + (void (*)(void *))OSSL_ENCODER_free); +} + +/* Create and populate a encoder method */ +static void *encoder_from_dispatch(int id, const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov) +{ + OSSL_ENCODER *encoder = NULL; + const OSSL_DISPATCH *fns = algodef->implementation; + + if ((encoder = ossl_encoder_new()) == NULL) + return NULL; + encoder->base.id = id; + encoder->base.propdef = algodef->property_definition; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_FUNC_ENCODER_NEWCTX: + if (encoder->newctx == NULL) + encoder->newctx = + OSSL_FUNC_encoder_newctx(fns); + break; + case OSSL_FUNC_ENCODER_FREECTX: + if (encoder->freectx == NULL) + encoder->freectx = + OSSL_FUNC_encoder_freectx(fns); + break; + case OSSL_FUNC_ENCODER_SET_CTX_PARAMS: + if (encoder->set_ctx_params == NULL) + encoder->set_ctx_params = + OSSL_FUNC_encoder_set_ctx_params(fns); + break; + case OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS: + if (encoder->settable_ctx_params == NULL) + 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); + break; + case OSSL_FUNC_ENCODER_ENCODE_OBJECT: + if (encoder->encode_object == NULL) + encoder->encode_object = + OSSL_FUNC_encoder_encode_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. + */ + if (!((encoder->newctx == NULL && encoder->freectx == NULL) + || (encoder->newctx != NULL && encoder->freectx != NULL)) + || (encoder->encode_data == NULL && encoder->encode_object == NULL)) { + OSSL_ENCODER_free(encoder); + ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INVALID_PROVIDER_FUNCTIONS); + return NULL; + } + + if (prov != NULL && !ossl_provider_up_ref(prov)) { + OSSL_ENCODER_free(encoder); + return NULL; + } + + encoder->base.prov = prov; + return encoder; +} + + +/* + * The core fetching functionality passes the names of the implementation. + * This function is responsible to getting an identity number for them, + * then call encoder_from_dispatch() with that identity number. + */ +static void *construct_encoder(const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov, void *unused) +{ + /* + * This function is only called if get_encoder_from_store() returned + * NULL, so it's safe to say that of all the spots to create a new + * namemap entry, this is it. Should the name already exist there, we + * know that ossl_namemap_add() will return its corresponding number. + */ + OPENSSL_CTX *libctx = ossl_provider_library_context(prov); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + const char *names = algodef->algorithm_names; + int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR); + void *method = NULL; + + if (id != 0) + method = encoder_from_dispatch(id, algodef, prov); + + return method; +} + +/* Intermediary function to avoid ugly casts, used