2020-08-16 21:25:08 +02:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
2020-09-07 12:25:17 +02:00
|
|
|
return ossl_pw_set_passphrase(&ctx->pwdata, kstr, klen);
|
2020-08-16 21:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
|
|
|
|
const UI_METHOD *ui_method,
|
|
|
|
void *ui_data)
|
|
|
|
{
|
2020-08-02 12:14:19 +02:00
|
|
|
return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
|
2020-08-16 21:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
|
|
|
|
pem_password_cb *cb, void *cbarg)
|
|
|
|
{
|
2020-08-02 12:14:19 +02:00
|
|
|
return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg);
|
2020-08-16 21:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2020-08-21 13:08:18 +02:00
|
|
|
return ctx->encoder->encode_data(ctx->encoderctx, params,
|
|
|
|
(OSSL_CORE_BIO *)out,
|
2020-08-02 12:14:19 +02:00
|
|
|
ossl_pw_passphrase_callback_enc,
|
|
|
|
&ctx->pwdata);
|
2020-08-16 21:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
|
2020-08-21 13:08:18 +02:00
|
|
|
return ctx->encoder->encode_object(ctx->encoderctx, keydata,
|
2020-08-16 21:25:08 +02:00
|
|
|
(OSSL_CORE_BIO *)out,
|
2020-08-02 12:14:19 +02:00
|
|
|
ossl_pw_passphrase_callback_enc,
|
|
|
|
&ctx->pwdata);
|
2020-08-16 21:25:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
2020-08-21 13:08:18 +02:00
|
|
|
* encoders inside EVP_KEYMGMT_names_do_all() due to locking order
|
|
|
|
* inversions with the store lock.
|
2020-08-16 21:25:08 +02:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|