/* * 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 #include "internal/provider.h" #include "internal/property.h" #include "crypto/evp.h" #include "encoder_local.h" DEFINE_STACK_OF_CSTRING() int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx, const char *cipher_name, const char *propquery) { OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_CIPHER, (void *)cipher_name, 0); params[1] = OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES, (void *)propquery, 0); return OSSL_ENCODER_CTX_set_params(ctx, params); } int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx, const unsigned char *kstr, size_t klen) { OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; params[0] = OSSL_PARAM_construct_octet_string(OSSL_ENCODER_PARAM_PASS, (void *)kstr, klen); return OSSL_ENCODER_CTX_set_params(ctx, params); } int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx, const UI_METHOD *ui_method, void *ui_data) { 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) { return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg); } /* * Support for OSSL_ENCODER_CTX_new_by_TYPE: * finding a suitable encoder */ struct selected_encoder_st { STACK_OF(OPENSSL_CSTRING) *names; int error; }; static void cache_encoders(const char *name, void *data) { struct selected_encoder_st *d = data; if (sk_OPENSSL_CSTRING_push(d->names, name) <= 0) d->error = 1; } /* * Support for OSSL_ENCODER_to_bio: * writing callback for the OSSL_PARAM (the implementation doesn't have * intimate knowledge of the provider side object) */ struct encoder_write_data_st { OSSL_ENCODER_CTX *ctx; BIO *out; }; static int encoder_write_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->serctx, params, (OSSL_CORE_BIO *)out, ossl_pw_passphrase_callback_enc, &ctx->pwdata); } /* * Support for OSSL_ENCODER_to_bio: * Perform the actual output. */ static int encoder_EVP_PKEY_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out) { const EVP_PKEY *pkey = ctx->object; void *keydata = pkey->keydata; EVP_KEYMGMT *keymgmt = pkey->keymgmt; /* * 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; if (ctx->encoder->encode_object == NULL || (OSSL_ENCODER_provider(ctx->encoder) != EVP_KEYMGMT_provider(keymgmt))) { struct encoder_write_data_st write_data; write_data.ctx = ctx; write_data.out = out; return evp_keymgmt_export(keymgmt, keydata, ctx->selection, &encoder_write_cb, &write_data); } return ctx->encoder->encode_object(ctx->serctx, keydata, (OSSL_CORE_BIO *)out, ossl_pw_passphrase_callback_enc, &ctx->pwdata); } /* * 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(), * 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) { OSSL_ENCODER_CTX *ctx = NULL; OSSL_ENCODER *encoder = NULL; EVP_KEYMGMT *keymgmt = pkey->keymgmt; int selection = OSSL_KEYMGMT_SELECT_ALL; if (!ossl_assert(pkey != NULL && propquery != NULL)) { ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); return NULL; } 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; int i; /* * Select the encoder in two steps. First, get the names of all of * the encoders. Then determine which is the best one to use. * This has to be broken because it isn't possible to fetch the * serialisers inside EVP_KEYMGMT_names_do_all() due to locking * order inversions with the store lock. */ sel_data.error = 0; sel_data.names = sk_OPENSSL_CSTRING_new_null(); if (sel_data.names == NULL) return NULL; EVP_KEYMGMT_names_do_all(keymgmt, cache_encoders, &sel_data); /* * Ignore memory allocation errors that are indicated in sel_data.error * in case a suitable provider does get found regardless. */ /* * Encoders offer two functions, one that handles object data in * the form of a OSSL_PARAM array, and one that directly handles a * provider side object. The latter requires that the encoder * is offered by the same provider that holds that object, but is * more desirable because it usually provides faster encoding. * * When looking up possible encoders, we save the first that can * handle an OSSL_PARAM array in |first| and use that if nothing * better turns up. */ for (i = 0; i < sk_OPENSSL_CSTRING_num(sel_data.names); i++) { name = sk_OPENSSL_CSTRING_value(sel_data.names, i); encoder = OSSL_ENCODER_fetch(libctx, name, propquery); if (encoder != NULL) { if (OSSL_ENCODER_provider(encoder) == desired_prov && encoder->encode_object != NULL) { OSSL_ENCODER_free(first); break; } if (first == NULL && encoder->encode_data != NULL) first = encoder; else OSSL_ENCODER_free(encoder); encoder = NULL; } } sk_OPENSSL_CSTRING_free(sel_data.names); if (encoder == NULL) encoder = first; if (encoder != NULL) { OSSL_PROPERTY_LIST *check = NULL, *current_props = NULL; check = ossl_parse_query(libctx, "type=parameters"); current_props = ossl_parse_property(libctx, OSSL_ENCODER_properties(encoder)); if (ossl_property_match_count(check, current_props) > 0) selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS; ossl_property_free(current_props); ossl_property_free(check); } else { if (sel_data.error) ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); else ERR_raise(ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_ENCODER_NOT_FOUND); } } ctx = OSSL_ENCODER_CTX_new(encoder); /* refcnt(encoder)++ */ OSSL_ENCODER_free(encoder); /* refcnt(encoder)-- */ if (ctx != NULL) { /* Setup for OSSL_ENCODE_to_bio() */ ctx->selection = selection; ctx->object = pkey; ctx->do_output = encoder_EVP_PKEY_to_bio; } return ctx; }