summaryrefslogtreecommitdiffstats
path: root/crypto/encode_decode/encoder_pkey.c
blob: 7c63a76adbb85344b18ec09574a7df6bf5cb32f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*
 * 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"

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;
}