2 * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
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
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"
24 DEFINE_STACK_OF(OSSL_ENCODER
)
26 int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX
*ctx
,
27 const char *cipher_name
,
28 const char *propquery
)
30 OSSL_PARAM params
[] = { OSSL_PARAM_END
, OSSL_PARAM_END
, OSSL_PARAM_END
};
33 OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_CIPHER
,
34 (void *)cipher_name
, 0);
36 OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES
,
37 (void *)propquery
, 0);
39 return OSSL_ENCODER_CTX_set_params(ctx
, params
);
42 int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX
*ctx
,
43 const unsigned char *kstr
,
46 return ossl_pw_set_passphrase(&ctx
->pwdata
, kstr
, klen
);
49 int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX
*ctx
,
50 const UI_METHOD
*ui_method
,
53 return ossl_pw_set_ui_method(&ctx
->pwdata
, ui_method
, ui_data
);
56 int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX
*ctx
,
57 pem_password_cb
*cb
, void *cbarg
)
59 return ossl_pw_set_pem_password_cb(&ctx
->pwdata
, cb
, cbarg
);
62 int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX
*ctx
,
63 OSSL_PASSPHRASE_CALLBACK
*cb
,
66 return ossl_pw_set_ossl_passphrase_cb(&ctx
->pwdata
, cb
, cbarg
);
70 * Support for OSSL_ENCODER_CTX_new_for_type:
71 * finding a suitable encoder
74 struct collected_encoder_st
{
75 STACK_OF(OPENSSL_CSTRING
) *names
;
77 const char *output_structure
;
78 const char *output_type
;
80 const OSSL_PROVIDER
*keymgmt_prov
;
81 OSSL_ENCODER_CTX
*ctx
;
82 unsigned int flag_find_same_provider
:1;
87 static void collect_encoder(OSSL_ENCODER
*encoder
, void *arg
)
89 struct collected_encoder_st
*data
= arg
;
90 const OSSL_PROVIDER
*prov
;
92 if (data
->error_occurred
)
95 data
->error_occurred
= 1; /* Assume the worst */
97 prov
= OSSL_ENCODER_get0_provider(encoder
);
99 * collect_encoder() is called in two passes, one where the encoders
100 * from the same provider as the keymgmt are looked up, and one where
101 * the other encoders are looked up. |data->flag_find_same_provider|
102 * tells us which pass we're in.
104 if ((data
->keymgmt_prov
== prov
) == data
->flag_find_same_provider
) {
105 void *provctx
= OSSL_PROVIDER_get0_provider_ctx(prov
);
106 int i
, end_i
= sk_OPENSSL_CSTRING_num(data
->names
);
109 for (i
= 0; i
< end_i
; i
++) {
110 if (data
->flag_find_same_provider
)
111 match
= (data
->id_names
[i
] == encoder
->base
.id
);
113 match
= OSSL_ENCODER_is_a(encoder
,
114 sk_OPENSSL_CSTRING_value(data
->names
, i
));
116 || (encoder
->does_selection
!= NULL
117 && !encoder
->does_selection(provctx
, data
->ctx
->selection
))
118 || (data
->keymgmt_prov
!= prov
119 && encoder
->import_object
== NULL
))
122 /* Only add each encoder implementation once */
123 if (OSSL_ENCODER_CTX_add_encoder(data
->ctx
, encoder
))
128 data
->error_occurred
= 0; /* All is good now */
131 struct collected_names_st
{
132 STACK_OF(OPENSSL_CSTRING
) *names
;
133 unsigned int error_occurred
:1;
136 static void collect_name(const char *name
, void *arg
)
138 struct collected_names_st
*data
= arg
;
140 if (data
->error_occurred
)
143 data
->error_occurred
= 1; /* Assume the worst */
145 if (sk_OPENSSL_CSTRING_push(data
->names
, name
) <= 0)
148 data
->error_occurred
= 0; /* All is good now */
152 * Support for OSSL_ENCODER_to_bio:
153 * writing callback for the OSSL_PARAM (the implementation doesn't have
154 * intimate knowledge of the provider side object)
157 struct construct_data_st
{
161 OSSL_ENCODER_INSTANCE
*encoder_inst
;
163 void *constructed_obj
;
166 static int encoder_import_cb(const OSSL_PARAM params
[], void *arg
)
168 struct construct_data_st
*construct_data
= arg
;
169 OSSL_ENCODER_INSTANCE
*encoder_inst
= construct_data
->encoder_inst
;
170 OSSL_ENCODER
*encoder
= OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst
);
171 void *encoderctx
= OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst
);
173 construct_data
->constructed_obj
=
174 encoder
->import_object(encoderctx
, construct_data
->selection
, params
);
176 return (construct_data
->constructed_obj
!= NULL
);
180 encoder_construct_pkey(OSSL_ENCODER_INSTANCE
*encoder_inst
, void *arg
)
182 struct construct_data_st
*data
= arg
;
184 if (data
->obj
== NULL
) {
185 OSSL_ENCODER
*encoder
=
186 OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst
);
187 const EVP_PKEY
*pk
= data
->pk
;
188 const OSSL_PROVIDER
*k_prov
= EVP_KEYMGMT_get0_provider(pk
->keymgmt
);
189 const OSSL_PROVIDER
*e_prov
= OSSL_ENCODER_get0_provider(encoder
);
191 if (k_prov
!= e_prov
) {
192 int selection
= data
->selection
;
194 if ((selection
& OSSL_KEYMGMT_SELECT_PRIVATE_KEY
) != 0)
195 selection
|= OSSL_KEYMGMT_SELECT_PUBLIC_KEY
;
196 data
->encoder_inst
= encoder_inst
;
198 if (!evp_keymgmt_export(pk
->keymgmt
, pk
->keydata
, selection
,
199 &encoder_import_cb
, data
))
201 data
->obj
= data
->constructed_obj
;
203 data
->obj
= pk
->keydata
;
210 static void encoder_destruct_pkey(void *arg
)
212 struct construct_data_st
*data
= arg
;
213 int match
= (data
->obj
== data
->constructed_obj
);
215 if (data
->encoder_inst
!= NULL
) {
216 OSSL_ENCODER
*encoder
=
217 OSSL_ENCODER_INSTANCE_get_encoder(data
->encoder_inst
);
219 encoder
->free_object(data
->constructed_obj
);
221 data
->constructed_obj
= NULL
;
227 * OSSL_ENCODER_CTX_new_for_pkey() returns a ctx with no encoder if
228 * it couldn't find a suitable encoder. This allows a caller to detect if
229 * a suitable encoder was found, with OSSL_ENCODER_CTX_get_num_encoder(),
230 * and to use fallback methods if the result is NULL.
232 static int ossl_encoder_ctx_setup_for_pkey(OSSL_ENCODER_CTX
*ctx
,
233 const EVP_PKEY
*pkey
,
235 const char *propquery
)
237 struct construct_data_st
*data
= NULL
;
238 const OSSL_PROVIDER
*prov
= NULL
;
239 OSSL_LIB_CTX
*libctx
= NULL
;
241 OSSL_NAMEMAP
*namemap
;
243 if (!ossl_assert(ctx
!= NULL
) || !ossl_assert(pkey
!= NULL
)) {
244 ERR_raise(ERR_LIB_OSSL_ENCODER
, ERR_R_PASSED_NULL_PARAMETER
);
248 if (evp_pkey_is_provided(pkey
)) {
249 prov
= EVP_KEYMGMT_get0_provider(pkey
->keymgmt
);
250 libctx
= ossl_provider_libctx(prov
);
253 if (pkey
->keymgmt
!= NULL
) {
254 struct collected_encoder_st encoder_data
;
255 struct collected_names_st keymgmt_data
;
257 if ((data
= OPENSSL_zalloc(sizeof(*data
))) == NULL
)
261 * Select the first encoder implementations in two steps.
262 * First, collect the keymgmt names, then the encoders that match.
264 keymgmt_data
.names
= sk_OPENSSL_CSTRING_new_null();
265 if (keymgmt_data
.names
== NULL
) {
266 ERR_raise(ERR_LIB_OSSL_ENCODER
, ERR_R_CRYPTO_LIB
);
270 keymgmt_data
.error_occurred
= 0;
271 EVP_KEYMGMT_names_do_all(pkey
->keymgmt
, collect_name
, &keymgmt_data
);
272 if (keymgmt_data
.error_occurred
) {
273 sk_OPENSSL_CSTRING_free(keymgmt_data
.names
);
277 encoder_data
.names
= keymgmt_data
.names
;
278 encoder_data
.output_type
= ctx
->output_type
;
279 encoder_data
.output_structure
= ctx
->output_structure
;
280 encoder_data
.error_occurred
= 0;
281 encoder_data
.keymgmt_prov
= prov
;
282 encoder_data
.ctx
= ctx
;
283 encoder_data
.id_names
= NULL
;
286 * collect_encoder() is called many times, and for every call it converts all encoder_data.names
287 * into namemap ids if it calls OSSL_ENCODER_is_a(). We cache the ids here instead,
288 * and can use them for encoders with the same provider as the keymgmt.
290 namemap
= ossl_namemap_stored(libctx
);
291 end
= sk_OPENSSL_CSTRING_num(encoder_data
.names
);
293 encoder_data
.id_names
= OPENSSL_malloc(end
* sizeof(int));
294 if (encoder_data
.id_names
== NULL
) {
295 sk_OPENSSL_CSTRING_free(keymgmt_data
.names
);
298 for (i
= 0; i
< end
; ++i
) {
299 const char *name
= sk_OPENSSL_CSTRING_value(keymgmt_data
.names
, i
);
301 encoder_data
.id_names
[i
] = ossl_namemap_name2num(namemap
, name
);
305 * Place the encoders with the a different provider as the keymgmt
306 * last (the chain is processed in reverse order)
308 encoder_data
.flag_find_same_provider
= 0;
309 OSSL_ENCODER_do_all_provided(libctx
, collect_encoder
, &encoder_data
);
312 * Place the encoders with the same provider as the keymgmt first
313 * (the chain is processed in reverse order)
315 encoder_data
.flag_find_same_provider
= 1;
316 OSSL_ENCODER_do_all_provided(libctx
, collect_encoder
, &encoder_data
);
318 OPENSSL_free(encoder_data
.id_names
);
319 sk_OPENSSL_CSTRING_free(keymgmt_data
.names
);
320 if (encoder_data
.error_occurred
) {
321 ERR_raise(ERR_LIB_OSSL_ENCODER
, ERR_R_CRYPTO_LIB
);
326 if (data
!= NULL
&& OSSL_ENCODER_CTX_get_num_encoders(ctx
) != 0) {
327 if (!OSSL_ENCODER_CTX_set_construct(ctx
, encoder_construct_pkey
)
328 || !OSSL_ENCODER_CTX_set_construct_data(ctx
, data
)
329 || !OSSL_ENCODER_CTX_set_cleanup(ctx
, encoder_destruct_pkey
))
333 data
->selection
= selection
;
335 data
= NULL
; /* Avoid it being freed */
341 OSSL_ENCODER_CTX_set_construct_data(ctx
, NULL
);
347 OSSL_ENCODER_CTX
*OSSL_ENCODER_CTX_new_for_pkey(const EVP_PKEY
*pkey
,
349 const char *output_type
,
350 const char *output_struct
,
351 const char *propquery
)
353 OSSL_ENCODER_CTX
*ctx
= NULL
;
354 OSSL_LIB_CTX
*libctx
= NULL
;
357 ERR_raise(ERR_LIB_OSSL_ENCODER
, ERR_R_PASSED_NULL_PARAMETER
);
361 if (!evp_pkey_is_assigned(pkey
)) {
362 ERR_raise_data(ERR_LIB_OSSL_ENCODER
, ERR_R_PASSED_INVALID_ARGUMENT
,
363 "The passed EVP_PKEY must be assigned a key");
367 if ((ctx
= OSSL_ENCODER_CTX_new()) == NULL
) {
368 ERR_raise(ERR_LIB_OSSL_ENCODER
, ERR_R_OSSL_ENCODER_LIB
);
372 if (evp_pkey_is_provided(pkey
)) {
373 const OSSL_PROVIDER
*prov
= EVP_KEYMGMT_get0_provider(pkey
->keymgmt
);
375 libctx
= ossl_provider_libctx(prov
);
378 OSSL_TRACE_BEGIN(ENCODER
) {
380 "(ctx %p) Looking for %s encoders with selection %d\n",
381 (void *)ctx
, EVP_PKEY_get0_type_name(pkey
), selection
);
382 BIO_printf(trc_out
, " output type: %s, output structure: %s\n",
383 output_type
, output_struct
);
384 } OSSL_TRACE_END(ENCODER
);
386 if (OSSL_ENCODER_CTX_set_output_type(ctx
, output_type
)
387 && (output_struct
== NULL
388 || OSSL_ENCODER_CTX_set_output_structure(ctx
, output_struct
))
389 && OSSL_ENCODER_CTX_set_selection(ctx
, selection
)
390 && ossl_encoder_ctx_setup_for_pkey(ctx
, pkey
, selection
, propquery
)
391 && OSSL_ENCODER_CTX_add_extra(ctx
, libctx
, propquery
)) {
392 OSSL_PARAM params
[2] = { OSSL_PARAM_END
, OSSL_PARAM_END
};
393 int save_parameters
= pkey
->save_parameters
;
395 params
[0] = OSSL_PARAM_construct_int(OSSL_ENCODER_PARAM_SAVE_PARAMETERS
,
397 /* ignoring error as this is only auxiliary parameter */
398 (void)OSSL_ENCODER_CTX_set_params(ctx
, params
);
400 OSSL_TRACE_BEGIN(ENCODER
) {
401 BIO_printf(trc_out
, "(ctx %p) Got %d encoders\n",
402 (void *)ctx
, OSSL_ENCODER_CTX_get_num_encoders(ctx
));
403 } OSSL_TRACE_END(ENCODER
);
407 OSSL_ENCODER_CTX_free(ctx
);