]>
Commit | Line | Data |
---|---|---|
ece9304c RL |
1 | /* |
2 | * Copyright 2019-2020 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/safestack.h> | |
16 | #include "internal/provider.h" | |
17 | #include "internal/property.h" | |
18 | #include "crypto/evp.h" | |
19 | #include "encoder_local.h" | |
20 | ||
ece9304c RL |
21 | int OSSL_ENCODER_CTX_set_cipher(OSSL_ENCODER_CTX *ctx, |
22 | const char *cipher_name, | |
23 | const char *propquery) | |
24 | { | |
25 | OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; | |
26 | ||
27 | params[0] = | |
28 | OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_CIPHER, | |
29 | (void *)cipher_name, 0); | |
30 | params[1] = | |
31 | OSSL_PARAM_construct_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES, | |
32 | (void *)propquery, 0); | |
33 | ||
34 | return OSSL_ENCODER_CTX_set_params(ctx, params); | |
35 | } | |
36 | ||
37 | int OSSL_ENCODER_CTX_set_passphrase(OSSL_ENCODER_CTX *ctx, | |
38 | const unsigned char *kstr, | |
39 | size_t klen) | |
40 | { | |
8ae40cf5 | 41 | return ossl_pw_set_passphrase(&ctx->pwdata, kstr, klen); |
ece9304c RL |
42 | } |
43 | ||
ece9304c RL |
44 | int OSSL_ENCODER_CTX_set_passphrase_ui(OSSL_ENCODER_CTX *ctx, |
45 | const UI_METHOD *ui_method, | |
46 | void *ui_data) | |
47 | { | |
a517edec | 48 | return ossl_pw_set_ui_method(&ctx->pwdata, ui_method, ui_data); |
ece9304c RL |
49 | } |
50 | ||
51 | int OSSL_ENCODER_CTX_set_passphrase_cb(OSSL_ENCODER_CTX *ctx, | |
52 | pem_password_cb *cb, void *cbarg) | |
53 | { | |
a517edec | 54 | return ossl_pw_set_pem_password_cb(&ctx->pwdata, cb, cbarg); |
ece9304c RL |
55 | } |
56 | ||
57 | /* | |
58 | * Support for OSSL_ENCODER_CTX_new_by_TYPE: | |
59 | * finding a suitable encoder | |
60 | */ | |
61 | ||
62 | struct selected_encoder_st { | |
63 | STACK_OF(OPENSSL_CSTRING) *names; | |
64 | int error; | |
65 | }; | |
66 | ||
67 | static void cache_encoders(const char *name, void *data) | |
68 | { | |
69 | struct selected_encoder_st *d = data; | |
70 | ||
71 | if (sk_OPENSSL_CSTRING_push(d->names, name) <= 0) | |
72 | d->error = 1; | |
73 | } | |
74 | ||
75 | /* | |
76 | * Support for OSSL_ENCODER_to_bio: | |
77 | * writing callback for the OSSL_PARAM (the implementation doesn't have | |
78 | * intimate knowledge of the provider side object) | |
79 | */ | |
80 | ||
81 | struct encoder_write_data_st { | |
82 | OSSL_ENCODER_CTX *ctx; | |
83 | BIO *out; | |
84 | }; | |
85 | ||
86 | static int encoder_write_cb(const OSSL_PARAM params[], void *arg) | |
87 | { | |
88 | struct encoder_write_data_st *write_data = arg; | |
89 | OSSL_ENCODER_CTX *ctx = write_data->ctx; | |
90 | BIO *out = write_data->out; | |
91 | ||
bd7a6f16 RL |
92 | return ctx->encoder->encode_data(ctx->encoderctx, params, |
93 | (OSSL_CORE_BIO *)out, | |
a517edec RL |
94 | ossl_pw_passphrase_callback_enc, |
95 | &ctx->pwdata); | |
ece9304c RL |
96 | } |
97 | ||
98 | /* | |
99 | * Support for OSSL_ENCODER_to_bio: | |
100 | * Perform the actual output. | |
101 | */ | |
102 | ||
103 | static int encoder_EVP_PKEY_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out) | |
104 | { | |
105 | const EVP_PKEY *pkey = ctx->object; | |
106 | void *keydata = pkey->keydata; | |
107 | EVP_KEYMGMT *keymgmt = pkey->keymgmt; | |
108 | ||
109 | /* | |
110 | * OSSL_ENCODER_CTX_new() creates a context, even when the | |
111 | * encoder it's given is NULL. Callers can detect the lack | |
112 | * of encoder with OSSL_ENCODER_CTX_get_encoder() and | |
113 | * should take precautions, possibly call a fallback instead of | |
114 | * OSSL_ENCODER_to_bio() / OSSL_ENCODER_to_fp(). If it's | |
115 | * come this far, we return an error. | |
116 | */ | |
117 | if (ctx->encoder == NULL) | |
118 | return 0; | |
119 | ||
120 | if (ctx->encoder->encode_object == NULL | |
121 | || (OSSL_ENCODER_provider(ctx->encoder) | |
122 | != EVP_KEYMGMT_provider(keymgmt))) { | |
123 | struct encoder_write_data_st write_data; | |
124 | ||
125 | write_data.ctx = ctx; | |
126 | write_data.out = out; | |
127 | ||
128 | return evp_keymgmt_export(keymgmt, keydata, ctx->selection, | |
129 | &encoder_write_cb, &write_data); | |
130 | } | |
131 | ||
bd7a6f16 | 132 | return ctx->encoder->encode_object(ctx->encoderctx, keydata, |
ece9304c | 133 | (OSSL_CORE_BIO *)out, |
a517edec RL |
134 | ossl_pw_passphrase_callback_enc, |
135 | &ctx->pwdata); | |
ece9304c RL |
136 | } |
137 | ||
138 | /* | |
139 | * OSSL_ENCODER_CTX_new_by_EVP_PKEY() returns a ctx with no encoder if | |
140 | * it couldn't find a suitable encoder. This allows a caller to detect if | |
141 | * a suitable encoder was found, with OSSL_ENCODER_CTX_get_encoder(), | |
142 | * and to use fallback methods if the result is NULL. | |
143 | */ | |
144 | OSSL_ENCODER_CTX *OSSL_ENCODER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey, | |
145 | const char *propquery) | |
146 | { | |
147 | OSSL_ENCODER_CTX *ctx = NULL; | |
148 | OSSL_ENCODER *encoder = NULL; | |
149 | EVP_KEYMGMT *keymgmt = pkey->keymgmt; | |
150 | int selection = OSSL_KEYMGMT_SELECT_ALL; | |
151 | ||
152 | if (!ossl_assert(pkey != NULL && propquery != NULL)) { | |
153 | ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); | |
154 | return NULL; | |
155 | } | |
156 | ||
157 | if (keymgmt != NULL) { | |
158 | const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(keymgmt); | |
159 | OPENSSL_CTX *libctx = ossl_provider_library_context(desired_prov); | |
160 | struct selected_encoder_st sel_data; | |
161 | OSSL_ENCODER *first = NULL; | |
162 | const char *name; | |
163 | int i; | |
164 | ||
165 | /* | |
166 | * Select the encoder in two steps. First, get the names of all of | |
167 | * the encoders. Then determine which is the best one to use. | |
168 | * This has to be broken because it isn't possible to fetch the | |
bd7a6f16 RL |
169 | * encoders inside EVP_KEYMGMT_names_do_all() due to locking order |
170 | * inversions with the store lock. | |
ece9304c RL |
171 | */ |
172 | sel_data.error = 0; | |
173 | sel_data.names = sk_OPENSSL_CSTRING_new_null(); | |
174 | if (sel_data.names == NULL) | |
175 | return NULL; | |
176 | EVP_KEYMGMT_names_do_all(keymgmt, cache_encoders, &sel_data); | |
177 | /* | |
178 | * Ignore memory allocation errors that are indicated in sel_data.error | |
179 | * in case a suitable provider does get found regardless. | |
180 | */ | |
181 | ||
182 | /* | |
183 | * Encoders offer two functions, one that handles object data in | |
184 | * the form of a OSSL_PARAM array, and one that directly handles a | |
185 | * provider side object. The latter requires that the encoder | |
186 | * is offered by the same provider that holds that object, but is | |
187 | * more desirable because it usually provides faster encoding. | |
188 | * | |
189 | * When looking up possible encoders, we save the first that can | |
190 | * handle an OSSL_PARAM array in |first| and use that if nothing | |
191 | * better turns up. | |
192 | */ | |
193 | for (i = 0; i < sk_OPENSSL_CSTRING_num(sel_data.names); i++) { | |
194 | name = sk_OPENSSL_CSTRING_value(sel_data.names, i); | |
195 | encoder = OSSL_ENCODER_fetch(libctx, name, propquery); | |
196 | if (encoder != NULL) { | |
197 | if (OSSL_ENCODER_provider(encoder) == desired_prov | |
198 | && encoder->encode_object != NULL) { | |
199 | OSSL_ENCODER_free(first); | |
200 | break; | |
201 | } | |
202 | if (first == NULL && encoder->encode_data != NULL) | |
203 | first = encoder; | |
204 | else | |
205 | OSSL_ENCODER_free(encoder); | |
206 | encoder = NULL; | |
207 | } | |
208 | } | |
209 | sk_OPENSSL_CSTRING_free(sel_data.names); | |
210 | if (encoder == NULL) | |
211 | encoder = first; | |
212 | ||
213 | if (encoder != NULL) { | |
214 | OSSL_PROPERTY_LIST *check = NULL, *current_props = NULL; | |
215 | ||
216 | check = ossl_parse_query(libctx, "type=parameters"); | |
217 | current_props = | |
218 | ossl_parse_property(libctx, OSSL_ENCODER_properties(encoder)); | |
219 | if (ossl_property_match_count(check, current_props) > 0) | |
220 | selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS; | |
221 | ossl_property_free(current_props); | |
222 | ossl_property_free(check); | |
223 | } else { | |
224 | if (sel_data.error) | |
225 | ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE); | |
226 | else | |
227 | ERR_raise(ERR_LIB_OSSL_ENCODER, | |
228 | OSSL_ENCODER_R_ENCODER_NOT_FOUND); | |
229 | } | |
230 | } | |
231 | ||
232 | ctx = OSSL_ENCODER_CTX_new(encoder); /* refcnt(encoder)++ */ | |
233 | OSSL_ENCODER_free(encoder); /* refcnt(encoder)-- */ | |
234 | ||
235 | if (ctx != NULL) { | |
236 | /* Setup for OSSL_ENCODE_to_bio() */ | |
237 | ctx->selection = selection; | |
238 | ctx->object = pkey; | |
239 | ctx->do_output = encoder_EVP_PKEY_to_bio; | |
240 | } | |
241 | ||
242 | return ctx; | |
243 | } | |
244 |