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