Coverage Report

Created: 2025-12-31 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl33/crypto/encode_decode/encoder_pkey.c
Line
Count
Source
1
/*
2
 * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
3
 *
4
 * Licensed under the Apache License 2.0 (the "License").  You may not use
5
 * this file except in compliance with the License.  You can obtain a copy
6
 * in the file LICENSE in the source distribution or at
7
 * https://www.openssl.org/source/license.html
8
 */
9
10
#include <openssl/err.h>
11
#include <openssl/ui.h>
12
#include <openssl/params.h>
13
#include <openssl/encoder.h>
14
#include <openssl/core_names.h>
15
#include <openssl/provider.h>
16
#include <openssl/safestack.h>
17
#include <openssl/trace.h>
18
#include "internal/provider.h"
19
#include "internal/property.h"
20
#include "internal/namemap.h"
21
#include "crypto/evp.h"
22
#include "encoder_local.h"
23
24
DEFINE_STACK_OF(OSSL_ENCODER)
25
26
int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx,
27
    const char *cipher_name,
28
    const char *propquery)
29
0
{
30
0
    OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
31
32
0
    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_CIPHER,
33
0
        (void *)cipher_name, 0);
34
0
    params[1] = OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES,
35
0
        (void *)propquery, 0);
36
37
0
    return OSSL_ENCODER_CTX_set_params(ctx, params);
38
0
}
39
40
int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx,
41
    const unsigned char *kstr,
42
    size_t klen)
43
0
{
44
0
    return ossl_pw_set_passphrase(&ctx->pwdata, kstr, klen);
45
0
}
46
47
int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
48
    const UI_METHOD *ui_method,
49
    void *ui_data)
50
0
{
51
0
    return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
52
0
}
53
54
int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX *ctx,
55
    pem_password_cb *cb, void *cbarg)
56
0
{
57
0
    return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg);
58
0
}
59
60
int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
61
    OSSL_PASSPHRASE_CALLBACK *cb,
62
    void *cbarg)
63
0
{
64
0
    return ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg);
65
0
}
66
67
/*
68
 * Support for OSSL_ENCODER_CTX_new_for_type:
69
 * finding a suitable encoder
70
 */
71
72
struct collected_encoder_st {
73
    STACK_OF(OPENSSL_CSTRING) *names;
74
    int *id_names;
75
    const char *output_structure;
76
    const char *output_type;
77
78
    const OSSL_PROVIDER *keymgmt_prov;
79
    OSSL_ENCODER_CTX *ctx;
80
    unsigned int flag_find_same_provider : 1;
81
82
    int error_occurred;
83
};
84
85
static void collect_encoder(OSSL_ENCODER *encoder, void *arg)
86
32.6M
{
87
32.6M
    struct collected_encoder_st *data = arg;
88
32.6M
    const OSSL_PROVIDER *prov;
89
90
32.6M
    if (data->error_occurred)
91
0
        return;
92
93
32.6M
    data->error_occurred = 1; /* Assume the worst */
94
95
32.6M
    prov = OSSL_ENCODER_get0_provider(encoder);
96
    /*
97
     * collect_encoder() is called in two passes, one where the encoders
98
     * from the same provider as the keymgmt are looked up, and one where
99
     * the other encoders are looked up.  |data->flag_find_same_provider|
100
     * tells us which pass we're in.
101
     */
102
32.6M
    if ((data->keymgmt_prov == prov) == data->flag_find_same_provider) {
103
16.3M
        void *provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
104
16.3M
        int i, end_i = sk_OPENSSL_CSTRING_num(data->names);
105
16.3M
        int match;
106
107
81.7M
        for (i = 0; i < end_i; i++) {
108
66.0M
            if (data->flag_find_same_provider)
109
66.0M
                match = (data->id_names[i] == encoder->base.id);
110
0
            else
111
0
                match = OSSL_ENCODER_is_a(encoder,
112
0
                    sk_OPENSSL_CSTRING_value(data->names, i));
113
66.0M
            if (!match
114
2.69M
                || (encoder->does_selection != NULL
115
2.60M
                    && !encoder->does_selection(provctx, data->ctx->selection))
116
677k
                || (data->keymgmt_prov != prov
117
0
                    && encoder->import_object == NULL))
118
65.3M
                continue;
119
120
            /* Only add each encoder implementation once */
121
677k
            if (OSSL_ENCODER_CTX_add_encoder(data->ctx, encoder))
122
677k
                break;
123
677k
        }
124
16.3M
    }
125
126
32.6M
    data->error_occurred = 0; /* All is good now */
127
32.6M
}
128
129
struct collected_names_st {
130
    STACK_OF(OPENSSL_CSTRING) *names;
131
    unsigned int error_occurred : 1;
132
};
133
134
static void collect_name(const char *name, void *arg)
135
396k
{
136
396k
    struct collected_names_st *data = arg;
137
138
396k
    if (data->error_occurred)
139
0
        return;
140
141
396k
    data->error_occurred = 1; /* Assume the worst */
142
143
396k
    if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0)
144
0
        return;
145
146
396k
    data->error_occurred = 0; /* All is good now */
147
396k
}
148
149
/*
150
 * Support for OSSL_ENCODER_to_bio:
151
 * writing callback for the OSSL_PARAM (the implementation doesn't have
152
 * intimate knowledge of the provider side object)
153
 */
154
155
struct construct_data_st {
156
    const EVP_PKEY *pk;
157
    int selection;
158
159
    OSSL_ENCODER_INSTANCE *encoder_inst;
160
    const void *obj;
161
    void *constructed_obj;
162
};
163
164
static int encoder_import_cb(const OSSL_PARAM params[], void *arg)
165
0
{
166
0
    struct construct_data_st *construct_data = arg;
167
0
    OSSL_ENCODER_INSTANCE *encoder_inst = construct_data->encoder_inst;
168
0
    OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
169
0
    void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
170
171
0
    construct_data->constructed_obj = encoder->import_object(encoderctx, construct_data->selection, params);
172
173
0
    return (construct_data->constructed_obj != NULL);
174
0
}
175
176
static const void *
177
encoder_construct_pkey(OSSL_ENCODER_INSTANCE *encoder_inst, void *arg)
178
91.5k
{
179
91.5k
    struct construct_data_st *data = arg;
180
181
91.5k
    if (data->obj == NULL) {
182
91.5k
        OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
183
91.5k
        const EVP_PKEY *pk = data->pk;
184
91.5k
        const OSSL_PROVIDER *k_prov = EVP_KEYMGMT_get0_provider(pk->keymgmt);
185
91.5k
        const OSSL_PROVIDER *e_prov = OSSL_ENCODER_get0_provider(encoder);
186
187
91.5k
        if (k_prov != e_prov) {
188
0
            int selection = data->selection;
189
190
0
            if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
191
0
                selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
192
0
            data->encoder_inst = encoder_inst;
193
194
0
            if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, selection,
195
0
                    &encoder_import_cb, data))
196
0
                return NULL;
197
0
            data->obj = data->constructed_obj;
198
91.5k
        } else {
199
91.5k
            data->obj = pk->keydata;
200
91.5k
        }
201
91.5k
    }
202
203
91.5k
    return data->obj;
204
91.5k
}
205
206
static void encoder_destruct_pkey(void *arg)
207
91.5k
{
208
91.5k
    struct construct_data_st *data = arg;
209
91.5k
    int match = (data->obj == data->constructed_obj);
210
211
91.5k
    if (data->encoder_inst != NULL) {
212
0
        OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(data->encoder_inst);
213
214
0
        encoder->free_object(data->constructed_obj);
215
0
    }
216
91.5k
    data->constructed_obj = NULL;
217
91.5k
    if (match)
218
0
        data->obj = NULL;
219
91.5k
}
220
221
/*
222
 * OSSL_ENCODER_CTX_new_for_pkey() returns a ctx with no encoder if
223
 * it couldn't find a suitable encoder.  This allows a caller to detect if
224
 * a suitable encoder was found, with OSSL_ENCODER_CTX_get_num_encoder(),
225
 * and to use fallback methods if the result is NULL.
226
 */
227
static int ossl_encoder_ctx_setup_for_pkey(OSSL_ENCODER_CTX *ctx,
228
    const EVP_PKEY *pkey,
229
    int selection,
230
    const char *propquery)
231
87.9k
{
232
87.9k
    struct construct_data_st *data = NULL;
233
87.9k
    const OSSL_PROVIDER *prov = NULL;
234
87.9k
    OSSL_LIB_CTX *libctx = NULL;
235
87.9k
    int ok = 0, i, end;
236
87.9k
    OSSL_NAMEMAP *namemap;
237
238
87.9k
    if (!ossl_assert(ctx != NULL) || !ossl_assert(pkey != NULL)) {
239
0
        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
240
0
        return 0;
241
0
    }
242
243
87.9k
    if (evp_pkey_is_provided(pkey)) {
244
86.6k
        prov = EVP_KEYMGMT_get0_provider(pkey->keymgmt);
245
86.6k
        libctx = ossl_provider_libctx(prov);
246
86.6k
    }
247
248
87.9k
    if (pkey->keymgmt != NULL) {
249
86.6k
        struct collected_encoder_st encoder_data;
250
86.6k
        struct collected_names_st keymgmt_data;
251
252
86.6k
        if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL)
253
0
            goto err;
254
255
        /*
256
         * Select the first encoder implementations in two steps.
257
         * First, collect the keymgmt names, then the encoders that match.
258
         */
259
86.6k
        keymgmt_data.names = sk_OPENSSL_CSTRING_new_null();
260
86.6k
        if (keymgmt_data.names == NULL) {
261
0
            ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_CRYPTO_LIB);
262
0
            goto err;
263
0
        }
264
265
86.6k
        keymgmt_data.error_occurred = 0;
266
86.6k
        EVP_KEYMGMT_names_do_all(pkey->keymgmt, collect_name, &keymgmt_data);
267
86.6k
        if (keymgmt_data.error_occurred) {
268
0
            sk_OPENSSL_CSTRING_free(keymgmt_data.names);
269
0
            goto err;
270
0
        }
271
272
86.6k
        encoder_data.names = keymgmt_data.names;
273
86.6k
        encoder_data.output_type = ctx->output_type;
274
86.6k
        encoder_data.output_structure = ctx->output_structure;
275
86.6k
        encoder_data.error_occurred = 0;
276
86.6k
        encoder_data.keymgmt_prov = prov;
277
86.6k
        encoder_data.ctx = ctx;
278
86.6k
        encoder_data.id_names = NULL;
279
280
        /*
281
         * collect_encoder() is called many times, and for every call it converts all encoder_data.names
282
         * into namemap ids if it calls OSSL_ENCODER_is_a(). We cache the ids here instead,
283
         * and can use them for encoders with the same provider as the keymgmt.
284
         */
285
86.6k
        namemap = ossl_namemap_stored(libctx);
286
86.6k
        end = sk_OPENSSL_CSTRING_num(encoder_data.names);
287
86.6k
        if (end > 0) {
288
86.6k
            encoder_data.id_names = OPENSSL_malloc(end * sizeof(int));
289
86.6k
            if (encoder_data.id_names == NULL) {
290
0
                sk_OPENSSL_CSTRING_free(keymgmt_data.names);
291
0
                goto err;
292
0
            }
293
455k
            for (i = 0; i < end; ++i) {
294
368k
                const char *name = sk_OPENSSL_CSTRING_value(keymgmt_data.names, i);
295
296
368k
                encoder_data.id_names[i] = ossl_namemap_name2num(namemap, name);
297
368k
            }
298
86.6k
        }
299
        /*
300
         * Place the encoders with the a different provider as the keymgmt
301
         * last (the chain is processed in reverse order)
302
         */
303
86.6k
        encoder_data.flag_find_same_provider = 0;
304
86.6k
        OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
305
306
        /*
307
         * Place the encoders with the same provider as the keymgmt first
308
         * (the chain is processed in reverse order)
309
         */
310
86.6k
        encoder_data.flag_find_same_provider = 1;
311
86.6k
        OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
312
313
86.6k
        OPENSSL_free(encoder_data.id_names);
314
86.6k
        sk_OPENSSL_CSTRING_free(keymgmt_data.names);
315
86.6k
        if (encoder_data.error_occurred) {
316
0
            ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_CRYPTO_LIB);
317
0
            goto err;
318
0
        }
319
86.6k
    }
320
321
87.9k
    if (data != NULL && OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0) {
322
86.6k
        if (!OSSL_ENCODER_CTX_set_construct(ctx, encoder_construct_pkey)
323
86.6k
            || !OSSL_ENCODER_CTX_set_construct_data(ctx, data)
324
86.6k
            || !OSSL_ENCODER_CTX_set_cleanup(ctx, encoder_destruct_pkey))
325
0
            goto err;
326
327
86.6k
        data->pk = pkey;
328
86.6k
        data->selection = selection;
329
330
86.6k
        data = NULL; /* Avoid it being freed */
331
86.6k
    }
332
333
87.9k
    ok = 1;
334
87.9k
err:
335
87.9k
    if (data != NULL) {
336
0
        OSSL_ENCODER_CTX_set_construct_data(ctx, NULL);
337
0
        OPENSSL_free(data);
338
0
    }
339
87.9k
    return ok;
340
87.9k
}
341
342
OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_for_pkey(const EVP_PKEY *pkey,
343
    int selection,
344
    const char *output_type,
345
    const char *output_struct,
346
    const char *propquery)
347
93.3k
{
348
93.3k
    OSSL_ENCODER_CTX *ctx = NULL;
349
93.3k
    OSSL_LIB_CTX *libctx = NULL;
350
351
93.3k
    if (pkey == NULL) {
352
0
        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
353
0
        return NULL;
354
0
    }
355
356
93.3k
    if (!evp_pkey_is_assigned(pkey)) {
357
0
        ERR_raise_data(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT,
358
0
            "The passed EVP_PKEY must be assigned a key");
359
0
        return NULL;
360
0
    }
361
362
93.3k
    if ((ctx = OSSL_ENCODER_CTX_new()) == NULL) {
363
0
        ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_OSSL_ENCODER_LIB);
364
0
        return NULL;
365
0
    }
366
367
93.3k
    if (evp_pkey_is_provided(pkey)) {
368
91.8k
        const OSSL_PROVIDER *prov = EVP_KEYMGMT_get0_provider(pkey->keymgmt);
369
370
91.8k
        libctx = ossl_provider_libctx(prov);
371
91.8k
    }
372
373
93.3k
    OSSL_TRACE_BEGIN(ENCODER)
374
0
    {
375
0
        BIO_printf(trc_out,
376
0
            "(ctx %p) Looking for %s encoders with selection %d\n",
377
0
            (void *)ctx, EVP_PKEY_get0_type_name(pkey), selection);
378
0
        BIO_printf(trc_out, "    output type: %s, output structure: %s\n",
379
0
            output_type, output_struct);
380
0
    }
381
93.3k
    OSSL_TRACE_END(ENCODER);
382
383
93.3k
    if (OSSL_ENCODER_CTX_set_output_type(ctx, output_type)
384
93.3k
        && (output_struct == NULL
385
11.3k
            || OSSL_ENCODER_CTX_set_output_structure(ctx, output_struct))
386
93.3k
        && OSSL_ENCODER_CTX_set_selection(ctx, selection)
387
93.3k
        && ossl_encoder_ctx_setup_for_pkey(ctx, pkey, selection, propquery)
388
93.3k
        && OSSL_ENCODER_CTX_add_extra(ctx, libctx, propquery)) {
389
93.3k
        OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
390
93.3k
        int save_parameters = pkey->save_parameters;
391
392
93.3k
        params[0] = OSSL_PARAM_construct_int(OSSL_ENCODER_PARAM_SAVE_PARAMETERS,
393
93.3k
            &save_parameters);
394
        /* ignoring error as this is only auxiliary parameter */
395
93.3k
        (void)OSSL_ENCODER_CTX_set_params(ctx, params);
396
397
93.3k
        OSSL_TRACE_BEGIN(ENCODER)
398
0
        {
399
0
            BIO_printf(trc_out, "(ctx %p) Got %d encoders\n",
400
0
                (void *)ctx, OSSL_ENCODER_CTX_get_num_encoders(ctx));
401
0
        }
402
93.3k
        OSSL_TRACE_END(ENCODER);
403
93.3k
        return ctx;
404
93.3k
    }
405
406
0
    OSSL_ENCODER_CTX_free(ctx);
407
    return NULL;
408
93.3k
}