openssl/crypto/encode_decode/encoder_pkey.c

247 lines
8.4 KiB
C
Raw Normal View History

/*
* 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 <openssl/err.h>
#include <openssl/ui.h>
#include <openssl/params.h>
#include <openssl/encoder.h>
#include <openssl/core_names.h>
#include <openssl/safestack.h>
#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)
{
return ossl_pw_set_passphrase(&ctx->pwdata, kstr, klen);
}
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->encoderctx, 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->encoderctx, 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
* encoders 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;
}