xref: /freebsd/crypto/openssl/crypto/encode_decode/encoder_pkey.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
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 
DEFINE_STACK_OF(OSSL_ENCODER)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] = OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_CIPHER,
33         (void *)cipher_name, 0);
34     params[1] = OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES,
35         (void *)propquery, 0);
36 
37     return OSSL_ENCODER_CTX_set_params(ctx, params);
38 }
39 
OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX * ctx,const unsigned char * kstr,size_t klen)40 int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx,
41     const unsigned char *kstr,
42     size_t klen)
43 {
44     return ossl_pw_set_passphrase(&ctx->pwdata, kstr, klen);
45 }
46 
OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX * ctx,const UI_METHOD * ui_method,void * ui_data)47 int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx,
48     const UI_METHOD *ui_method,
49     void *ui_data)
50 {
51     return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data);
52 }
53 
OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX * ctx,pem_password_cb * cb,void * cbarg)54 int OSSL_ENCODER_CTX_set_pem_password_cb(OSSL_ENCODER_CTX *ctx,
55     pem_password_cb *cb, void *cbarg)
56 {
57     return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg);
58 }
59 
OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX * ctx,OSSL_PASSPHRASE_CALLBACK * cb,void * cbarg)60 int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx,
61     OSSL_PASSPHRASE_CALLBACK *cb,
62     void *cbarg)
63 {
64     return ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, cb, cbarg);
65 }
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 
collect_encoder(OSSL_ENCODER * encoder,void * arg)85 static void collect_encoder(OSSL_ENCODER *encoder, void *arg)
86 {
87     struct collected_encoder_st *data = arg;
88     const OSSL_PROVIDER *prov;
89 
90     if (data->error_occurred)
91         return;
92 
93     data->error_occurred = 1; /* Assume the worst */
94 
95     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     if ((data->keymgmt_prov == prov) == data->flag_find_same_provider) {
103         void *provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
104         int i, end_i = sk_OPENSSL_CSTRING_num(data->names);
105         int match;
106 
107         for (i = 0; i < end_i; i++) {
108             if (data->flag_find_same_provider)
109                 match = (data->id_names[i] == encoder->base.id);
110             else
111                 match = OSSL_ENCODER_is_a(encoder,
112                     sk_OPENSSL_CSTRING_value(data->names, i));
113             if (!match
114                 || (encoder->does_selection != NULL
115                     && !encoder->does_selection(provctx, data->ctx->selection))
116                 || (data->keymgmt_prov != prov
117                     && encoder->import_object == NULL))
118                 continue;
119 
120             /* Only add each encoder implementation once */
121             if (OSSL_ENCODER_CTX_add_encoder(data->ctx, encoder))
122                 break;
123         }
124     }
125 
126     data->error_occurred = 0; /* All is good now */
127 }
128 
129 struct collected_names_st {
130     STACK_OF(OPENSSL_CSTRING) *names;
131     unsigned int error_occurred : 1;
132 };
133 
collect_name(const char * name,void * arg)134 static void collect_name(const char *name, void *arg)
135 {
136     struct collected_names_st *data = arg;
137 
138     if (data->error_occurred)
139         return;
140 
141     data->error_occurred = 1; /* Assume the worst */
142 
143     if (sk_OPENSSL_CSTRING_push(data->names, name) <= 0)
144         return;
145 
146     data->error_occurred = 0; /* All is good now */
147 }
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 
encoder_import_cb(const OSSL_PARAM params[],void * arg)164 static int encoder_import_cb(const OSSL_PARAM params[], void *arg)
165 {
166     struct construct_data_st *construct_data = arg;
167     OSSL_ENCODER_INSTANCE *encoder_inst = construct_data->encoder_inst;
168     OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
169     void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
170 
171     construct_data->constructed_obj = encoder->import_object(encoderctx, construct_data->selection, params);
172 
173     return (construct_data->constructed_obj != NULL);
174 }
175 
176 static const void *
encoder_construct_pkey(OSSL_ENCODER_INSTANCE * encoder_inst,void * arg)177 encoder_construct_pkey(OSSL_ENCODER_INSTANCE *encoder_inst, void *arg)
178 {
179     struct construct_data_st *data = arg;
180 
181     if (data->obj == NULL) {
182         OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
183         const EVP_PKEY *pk = data->pk;
184         const OSSL_PROVIDER *k_prov = EVP_KEYMGMT_get0_provider(pk->keymgmt);
185         const OSSL_PROVIDER *e_prov = OSSL_ENCODER_get0_provider(encoder);
186 
187         if (k_prov != e_prov) {
188             int selection = data->selection;
189 
190             if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
191                 selection |= OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
192             data->encoder_inst = encoder_inst;
193 
194             if (!evp_keymgmt_export(pk->keymgmt, pk->keydata, selection,
195                     &encoder_import_cb, data))
196                 return NULL;
197             data->obj = data->constructed_obj;
198         } else {
199             data->obj = pk->keydata;
200         }
201     }
202 
203     return data->obj;
204 }
205 
encoder_destruct_pkey(void * arg)206 static void encoder_destruct_pkey(void *arg)
207 {
208     struct construct_data_st *data = arg;
209     int match = (data->obj == data->constructed_obj);
210 
211     if (data->encoder_inst != NULL) {
212         OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(data->encoder_inst);
213 
214         encoder->free_object(data->constructed_obj);
215     }
216     data->constructed_obj = NULL;
217     if (match)
218         data->obj = NULL;
219 }
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  */
ossl_encoder_ctx_setup_for_pkey(OSSL_ENCODER_CTX * ctx,const EVP_PKEY * pkey,int selection,const char * propquery)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 {
232     struct construct_data_st *data = NULL;
233     const OSSL_PROVIDER *prov = NULL;
234     OSSL_LIB_CTX *libctx = NULL;
235     int ok = 0, i, end;
236     OSSL_NAMEMAP *namemap;
237 
238     if (!ossl_assert(ctx != NULL) || !ossl_assert(pkey != NULL)) {
239         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
240         return 0;
241     }
242 
243     if (evp_pkey_is_provided(pkey)) {
244         prov = EVP_KEYMGMT_get0_provider(pkey->keymgmt);
245         libctx = ossl_provider_libctx(prov);
246     }
247 
248     if (pkey->keymgmt != NULL) {
249         struct collected_encoder_st encoder_data;
250         struct collected_names_st keymgmt_data;
251 
252         if ((data = OPENSSL_zalloc(sizeof(*data))) == NULL)
253             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         keymgmt_data.names = sk_OPENSSL_CSTRING_new_null();
260         if (keymgmt_data.names == NULL) {
261             ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_CRYPTO_LIB);
262             goto err;
263         }
264 
265         keymgmt_data.error_occurred = 0;
266         EVP_KEYMGMT_names_do_all(pkey->keymgmt, collect_name, &keymgmt_data);
267         if (keymgmt_data.error_occurred) {
268             sk_OPENSSL_CSTRING_free(keymgmt_data.names);
269             goto err;
270         }
271 
272         encoder_data.names = keymgmt_data.names;
273         encoder_data.output_type = ctx->output_type;
274         encoder_data.output_structure = ctx->output_structure;
275         encoder_data.error_occurred = 0;
276         encoder_data.keymgmt_prov = prov;
277         encoder_data.ctx = ctx;
278         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         namemap = ossl_namemap_stored(libctx);
286         end = sk_OPENSSL_CSTRING_num(encoder_data.names);
287         if (end > 0) {
288             encoder_data.id_names = OPENSSL_malloc(end * sizeof(int));
289             if (encoder_data.id_names == NULL) {
290                 sk_OPENSSL_CSTRING_free(keymgmt_data.names);
291                 goto err;
292             }
293             for (i = 0; i < end; ++i) {
294                 const char *name = sk_OPENSSL_CSTRING_value(keymgmt_data.names, i);
295 
296                 encoder_data.id_names[i] = ossl_namemap_name2num(namemap, name);
297             }
298         }
299         /*
300          * Place the encoders with the a different provider as the keymgmt
301          * last (the chain is processed in reverse order)
302          */
303         encoder_data.flag_find_same_provider = 0;
304         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         encoder_data.flag_find_same_provider = 1;
311         OSSL_ENCODER_do_all_provided(libctx, collect_encoder, &encoder_data);
312 
313         OPENSSL_free(encoder_data.id_names);
314         sk_OPENSSL_CSTRING_free(keymgmt_data.names);
315         if (encoder_data.error_occurred) {
316             ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_CRYPTO_LIB);
317             goto err;
318         }
319     }
320 
321     if (data != NULL && OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0) {
322         if (!OSSL_ENCODER_CTX_set_construct(ctx, encoder_construct_pkey)
323             || !OSSL_ENCODER_CTX_set_construct_data(ctx, data)
324             || !OSSL_ENCODER_CTX_set_cleanup(ctx, encoder_destruct_pkey))
325             goto err;
326 
327         data->pk = pkey;
328         data->selection = selection;
329 
330         data = NULL; /* Avoid it being freed */
331     }
332 
333     ok = 1;
334 err:
335     if (data != NULL) {
336         OSSL_ENCODER_CTX_set_construct_data(ctx, NULL);
337         OPENSSL_free(data);
338     }
339     return ok;
340 }
341 
OSSL_ENCODER_CTX_new_for_pkey(const EVP_PKEY * pkey,int selection,const char * output_type,const char * output_struct,const char * propquery)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 {
348     OSSL_ENCODER_CTX *ctx = NULL;
349     OSSL_LIB_CTX *libctx = NULL;
350 
351     if (pkey == NULL) {
352         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
353         return NULL;
354     }
355 
356     if (!evp_pkey_is_assigned(pkey)) {
357         ERR_raise_data(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT,
358             "The passed EVP_PKEY must be assigned a key");
359         return NULL;
360     }
361 
362     if ((ctx = OSSL_ENCODER_CTX_new()) == NULL) {
363         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_OSSL_ENCODER_LIB);
364         return NULL;
365     }
366 
367     if (evp_pkey_is_provided(pkey)) {
368         const OSSL_PROVIDER *prov = EVP_KEYMGMT_get0_provider(pkey->keymgmt);
369 
370         libctx = ossl_provider_libctx(prov);
371     }
372 
373     OSSL_TRACE_BEGIN(ENCODER)
374     {
375         BIO_printf(trc_out,
376             "(ctx %p) Looking for %s encoders with selection %d\n",
377             (void *)ctx, EVP_PKEY_get0_type_name(pkey), selection);
378         BIO_printf(trc_out, "    output type: %s, output structure: %s\n",
379             output_type, output_struct);
380     }
381     OSSL_TRACE_END(ENCODER);
382 
383     if (OSSL_ENCODER_CTX_set_output_type(ctx, output_type)
384         && (output_struct == NULL
385             || OSSL_ENCODER_CTX_set_output_structure(ctx, output_struct))
386         && OSSL_ENCODER_CTX_set_selection(ctx, selection)
387         && ossl_encoder_ctx_setup_for_pkey(ctx, pkey, selection, propquery)
388         && OSSL_ENCODER_CTX_add_extra(ctx, libctx, propquery)) {
389         OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
390         int save_parameters = pkey->save_parameters;
391 
392         params[0] = OSSL_PARAM_construct_int(OSSL_ENCODER_PARAM_SAVE_PARAMETERS,
393             &save_parameters);
394         /* ignoring error as this is only auxiliary parameter */
395         (void)OSSL_ENCODER_CTX_set_params(ctx, params);
396 
397         OSSL_TRACE_BEGIN(ENCODER)
398         {
399             BIO_printf(trc_out, "(ctx %p) Got %d encoders\n",
400                 (void *)ctx, OSSL_ENCODER_CTX_get_num_encoders(ctx));
401         }
402         OSSL_TRACE_END(ENCODER);
403         return ctx;
404     }
405 
406     OSSL_ENCODER_CTX_free(ctx);
407     return NULL;
408 }
409