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 { 30 OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; 31 32 params[0] = 33 OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_CIPHER, 34 (void *)cipher_name, 0); 35 params[1] = 36 OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES, 37 (void *)propquery, 0); 38 39 return OSSL_ENCODER_CTX_set_params(ctx, params); 40 } 41 42 int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx, 43 const unsigned char *kstr, 44 size_t klen) 45 { 46 return ossl_pw_set_passphrase(&ctx->pwdata, kstr, klen); 47 } 48 49 int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx, 50 const UI_METHOD *ui_method, 51 void *ui_data) 52 { 53 return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data); 54 } 55 56 int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX *ctx, 57 pem_password_cb *cb, void *cbarg) 58 { 59 return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg); 60 } 61 62 int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx, 63 OSSL_PASSPHRASE_CALLBACK *cb, 64 void *cbarg) 65 { 66 return ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg); 67 } 68 69 /* 70 * Support for OSSL_ENCODER_CTX_new_for_type: 71 * finding a suitable encoder 72 */ 73 74 struct collected_encoder_st { 75 STACK_OF(OPENSSL_CSTRING) *names; 76 int *id_names; 77 const char *output_structure; 78 const char *output_type; 79 80 const OSSL_PROVIDER *keymgmt_prov; 81 OSSL_ENCODER_CTX *ctx; 82 unsigned int flag_find_same_provider:1; 83 84 int error_occurred; 85 }; 86 87 static void collect_encoder(OSSL_ENCODER *encoder, void *arg) 88 { 89 struct collected_encoder_st *data = arg; 90 const OSSL_PROVIDER *prov; 91 92 if (data->error_occurred) 93 return; 94 95 data->error_occurred = 1; /* Assume the worst */ 96 97 prov = OSSL_ENCODER_get0_provider(encoder); 98 /* 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. 103 */ 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); 107 int match; 108 109 for (i = 0; i < end_i; i++) { 110 if (data->flag_find_same_provider) 111 match = (data->id_names[i] == encoder->base.id); 112 else 113 match = OSSL_ENCODER_is_a(encoder, 114 sk_OPENSSL_CSTRING_value(data->names, i)); 115 if (!match 116 || (encoder->does_selection != NULL 117 && !encoder->does_selection(provctx, data->ctx->selection)) 118 || (data->keymgmt_prov != prov 119 && encoder->import_object == NULL)) 120 continue; 121 122 /* Only add each encoder implementation once */ 123 if (OSSL_ENCODER_CTX_add_encoder(data->ctx, encoder)) 124 break; 125 } 126 } 127 128 data->error_occurred = 0; /* All is good now */ 129 } 130 131 struct collected_names_st { 132 STACK_OF(OPENSSL_CSTRING) *names; 133 unsigned int error_occurred:1; 134 }; 135 136 static void collect_name(const char *name, void *arg) 137 { 138 struct collected_names_st *data = arg; 139 140 if (data->error_occurred) 141 return; 142 143 data->error_occurred = 1; /* Assume the worst */ 144 145 if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0) 146 return; 147 148 data->error_occurred = 0; /* All is good now */ 149 } 150 151 /* 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) 155 */ 156 157 struct construct_data_st { 158 const EVP_PKEY *pk; 159 int selection; 160 161 OSSL_ENCODER_INSTANCE *encoder_inst; 162 const void *obj; 163 void *constructed_obj; 164 }; 165 166 static int encoder_import_cb(const OSSL_PARAM params[], void *arg) 167 { 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); 172 173 construct_data->constructed_obj = 174 encoder->import_object(encoderctx, construct_data->selection, params); 175 176 return (construct_data->constructed_obj != NULL); 177 } 178 179 static const void * 180 encoder_construct_pkey(OSSL_ENCODER_INSTANCE *encoder_inst, void *arg) 181 { 182 struct construct_data_st *data = arg; 183 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); 190 191 if (k_prov != e_prov) { 192 int selection = data->selection; 193 194 if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) 195 selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY; 196 data->encoder_inst = encoder_inst; 197 198 if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, selection, 199 &encoder_import_cb, data)) 200 return NULL; 201 data->obj = data->constructed_obj; 202 } else { 203 data->obj = pk->keydata; 204 } 205 } 206 207 return data->obj; 208 } 209 210 static void encoder_destruct_pkey(void *arg) 211 { 212 struct construct_data_st *data = arg; 213 int match = (data->obj == data->constructed_obj); 214 215 if (data->encoder_inst != NULL) { 216 OSSL_ENCODER *encoder = 217 OSSL_ENCODER_INSTANCE_get_encoder(data->encoder_inst); 218 219 encoder->free_object(data->constructed_obj); 220 } 221 data->constructed_obj = NULL; 222 if (match) 223 data->obj = NULL; 224 } 225 226 /* 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. 231 */ 232 static int ossl_encoder_ctx_setup_for_pkey(OSSL_ENCODER_CTX *ctx, 233 const EVP_PKEY *pkey, 234 int selection, 235 const char *propquery) 236 { 237 struct construct_data_st *data = NULL; 238 const OSSL_PROVIDER *prov = NULL; 239 OSSL_LIB_CTX *libctx = NULL; 240 int ok = 0, i, end; 241 OSSL_NAMEMAP *namemap; 242 243 if (!ossl_assert(ctx != NULL) || !ossl_assert(pkey != NULL)) { 244 ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); 245 return 0; 246 } 247 248 if (evp_pkey_is_provided(pkey)) { 249 prov = EVP_KEYMGMT_get0_provider(pkey->keymgmt); 250 libctx = ossl_provider_libctx(prov); 251 } 252 253 if (pkey->keymgmt != NULL) { 254 struct collected_encoder_st encoder_data; 255 struct collected_names_st keymgmt_data; 256 257 if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL) 258 goto err; 259 260 /* 261 * Select the first encoder implementations in two steps. 262 * First, collect the keymgmt names, then the encoders that match. 263 */ 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); 267 goto err; 268 } 269 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); 274 goto err; 275 } 276 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; 284 285 /* 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. 289 */ 290 namemap = ossl_namemap_stored(libctx); 291 end = sk_OPENSSL_CSTRING_num(encoder_data.names); 292 if (end > 0) { 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); 296 goto err; 297 } 298 for (i = 0; i < end; ++i) { 299 const char *name = sk_OPENSSL_CSTRING_value(keymgmt_data.names, i); 300 301 encoder_data.id_names[i] = ossl_namemap_name2num(namemap, name); 302 } 303 } 304 /* 305 * Place the encoders with the a different provider as the keymgmt 306 * last (the chain is processed in reverse order) 307 */ 308 encoder_data.flag_find_same_provider = 0; 309 OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data); 310 311 /* 312 * Place the encoders with the same provider as the keymgmt first 313 * (the chain is processed in reverse order) 314 */ 315 encoder_data.flag_find_same_provider = 1; 316 OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data); 317 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); 322 goto err; 323 } 324 } 325 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)) 330 goto err; 331 332 data->pk = pkey; 333 data->selection = selection; 334 335 data = NULL; /* Avoid it being freed */ 336 } 337 338 ok = 1; 339 err: 340 if (data != NULL) { 341 OSSL_ENCODER_CTX_set_construct_data(ctx, NULL); 342 OPENSSL_free(data); 343 } 344 return ok; 345 } 346 347 OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_for_pkey(const EVP_PKEY *pkey, 348 int selection, 349 const char *output_type, 350 const char *output_struct, 351 const char *propquery) 352 { 353 OSSL_ENCODER_CTX *ctx = NULL; 354 OSSL_LIB_CTX *libctx = NULL; 355 356 if (pkey == NULL) { 357 ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); 358 return NULL; 359 } 360 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"); 364 return NULL; 365 } 366 367 if ((ctx = OSSL_ENCODER_CTX_new()) == NULL) { 368 ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_OSSL_ENCODER_LIB); 369 return NULL; 370 } 371 372 if (evp_pkey_is_provided(pkey)) { 373 const OSSL_PROVIDER *prov = EVP_KEYMGMT_get0_provider(pkey->keymgmt); 374 375 libctx = ossl_provider_libctx(prov); 376 } 377 378 OSSL_TRACE_BEGIN(ENCODER) { 379 BIO_printf(trc_out, 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); 385 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; 394 395 params[0] = OSSL_PARAM_construct_int(OSSL_ENCODER_PARAM_SAVE_PARAMETERS, 396 &save_parameters); 397 /* ignoring error as this is only auxiliary parameter */ 398 (void)OSSL_ENCODER_CTX_set_params(ctx, params); 399 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); 404 return ctx; 405 } 406 407 OSSL_ENCODER_CTX_free(ctx); 408 return NULL; 409 } 410