diff options
Diffstat (limited to 'crypto/encode_decode/decoder_pkey.c')
-rw-r--r-- | crypto/encode_decode/decoder_pkey.c | 374 |
1 files changed, 374 insertions, 0 deletions
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 <openssl/core_names.h> +#include <openssl/evp.h> +#include <openssl/ui.h> +#include <openssl/decoder.h> +#include <openssl/safestack.h> +#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; +} |