179c6d3dc371ffb9be2e93c6b8b3eb2821b13c51
[openssl.git] / crypto / encode_decode / encoder_lib.c
1 /*
2  * Copyright 2019 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 "e_os.h"                /* strcasecmp on Windows */
11 #include <openssl/core_names.h>
12 #include <openssl/bio.h>
13 #include <openssl/encoder.h>
14 #include <openssl/buffer.h>
15 #include <openssl/params.h>
16 #include <openssl/provider.h>
17 #include "encoder_local.h"
18
19 static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out);
20
21 int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out)
22 {
23     return encoder_process(ctx, out);
24 }
25
26 #ifndef OPENSSL_NO_STDIO
27 static BIO *bio_from_file(FILE *fp)
28 {
29     BIO *b;
30
31     if ((b = BIO_new(BIO_s_file())) == NULL) {
32         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_BUF_LIB);
33         return NULL;
34     }
35     BIO_set_fp(b, fp, BIO_NOCLOSE);
36     return b;
37 }
38
39 int OSSL_ENCODER_to_fp(OSSL_ENCODER_CTX *ctx, FILE *fp)
40 {
41     BIO *b = bio_from_file(fp);
42     int ret = 0;
43
44     if (b != NULL)
45         ret = OSSL_ENCODER_to_bio(ctx, b);
46
47     BIO_free(b);
48     return ret;
49 }
50 #endif
51
52 int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx,
53                                      const char *output_type)
54 {
55     if (!ossl_assert(ctx != NULL) || !ossl_assert(output_type != NULL)) {
56         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
57         return 0;
58     }
59
60     ctx->output_type = output_type;
61     return 1;
62 }
63
64 int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection)
65 {
66     if (!ossl_assert(ctx != NULL)) {
67         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
68         return 0;
69     }
70
71     if (!ossl_assert(selection != 0)) {
72         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT);
73         return 0;
74     }
75
76     ctx->selection = selection;
77     return 1;
78 }
79
80 static OSSL_ENCODER_INSTANCE *ossl_encoder_instance_new(OSSL_ENCODER *encoder,
81                                                         void *encoderctx)
82 {
83     OSSL_ENCODER_INSTANCE *encoder_inst = NULL;
84     OSSL_PARAM params[3];
85
86     if (!ossl_assert(encoder != NULL)) {
87         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
88         return 0;
89     }
90
91     if (encoder->get_params == NULL) {
92         ERR_raise(ERR_LIB_OSSL_ENCODER,
93                   OSSL_ENCODER_R_MISSING_GET_PARAMS);
94         return 0;
95     }
96
97     if ((encoder_inst = OPENSSL_zalloc(sizeof(*encoder_inst))) == NULL) {
98         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
99         return 0;
100     }
101
102     /*
103      * Cache the input and output types for this encoder.  The output type
104      * is mandatory.
105      */
106     params[0] =
107         OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_OUTPUT_TYPE,
108                                       (char **)&encoder_inst->output_type, 0);
109     params[1] =
110         OSSL_PARAM_construct_utf8_ptr(OSSL_ENCODER_PARAM_INPUT_TYPE,
111                                       (char **)&encoder_inst->input_type, 0);
112     params[2] = OSSL_PARAM_construct_end();
113
114     if (!encoder->get_params(params)
115         || !OSSL_PARAM_modified(&params[1]))
116         goto err;
117
118     if (!OSSL_ENCODER_up_ref(encoder)) {
119         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR);
120         goto err;
121     }
122
123     encoder_inst->encoder = encoder;
124     encoder_inst->encoderctx = encoderctx;
125     return encoder_inst;
126  err:
127     ossl_encoder_instance_free(encoder_inst);
128     return NULL;
129 }
130
131 void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst)
132 {
133     if (encoder_inst != NULL) {
134         if (encoder_inst->encoder != NULL)
135             encoder_inst->encoder->freectx(encoder_inst->encoderctx);
136         encoder_inst->encoderctx = NULL;
137         OSSL_ENCODER_free(encoder_inst->encoder);
138         encoder_inst->encoder = NULL;
139         OPENSSL_free(encoder_inst);
140     }
141 }
142
143 static int ossl_encoder_ctx_add_encoder_inst(OSSL_ENCODER_CTX *ctx,
144                                              OSSL_ENCODER_INSTANCE *ei)
145 {
146     if (ctx->encoder_insts == NULL
147         && (ctx->encoder_insts =
148             sk_OSSL_ENCODER_INSTANCE_new_null()) == NULL) {
149         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_MALLOC_FAILURE);
150         return 0;
151     }
152
153     return (sk_OSSL_ENCODER_INSTANCE_push(ctx->encoder_insts, ei) > 0);
154 }
155
156 int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder)
157 {
158     OSSL_ENCODER_INSTANCE *encoder_inst = NULL;
159     const OSSL_PROVIDER *prov = NULL;
160     void *encoderctx = NULL;
161     void *provctx = NULL;
162
163     if (!ossl_assert(ctx != NULL) || !ossl_assert(encoder != NULL)) {
164         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
165         return 0;
166     }
167
168     prov = OSSL_ENCODER_provider(encoder);
169     provctx = OSSL_PROVIDER_get0_provider_ctx(prov);
170
171     if ((encoderctx = encoder->newctx(provctx)) == NULL
172         || (encoder_inst =
173             ossl_encoder_instance_new(encoder, encoderctx)) == NULL)
174         goto err;
175     /* Avoid double free of encoderctx on further errors */
176     encoderctx = NULL;
177
178     if (!ossl_encoder_ctx_add_encoder_inst(ctx, encoder_inst))
179         goto err;
180
181     return 1;
182  err:
183     ossl_encoder_instance_free(encoder_inst);
184     if (encoderctx != NULL)
185         encoder->freectx(encoderctx);
186     return 0;
187 }
188
189 int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx,
190                                OPENSSL_CTX *libctx, const char *propq)
191 {
192     return 1;
193 }
194
195 int OSSL_ENCODER_CTX_get_num_encoders(OSSL_ENCODER_CTX *ctx)
196 {
197     if (ctx == NULL || ctx->encoder_insts == NULL)
198         return 0;
199     return sk_OSSL_ENCODER_INSTANCE_num(ctx->encoder_insts);
200 }
201
202 int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx,
203                                    OSSL_ENCODER_CONSTRUCT *construct)
204 {
205     if (!ossl_assert(ctx != NULL)) {
206         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
207         return 0;
208     }
209     ctx->construct = construct;
210     return 1;
211 }
212
213 int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx,
214                                         void *construct_data)
215 {
216     if (!ossl_assert(ctx != NULL)) {
217         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
218         return 0;
219     }
220     ctx->construct_data = construct_data;
221     return 1;
222 }
223
224 int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx,
225                                  OSSL_ENCODER_CLEANUP *cleanup)
226 {
227     if (!ossl_assert(ctx != NULL)) {
228         ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
229         return 0;
230     }
231     ctx->cleanup = cleanup;
232     return 1;
233 }
234
235 OSSL_ENCODER *
236 OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst)
237 {
238     if (encoder_inst == NULL)
239         return NULL;
240     return encoder_inst->encoder;
241 }
242
243 void *
244 OSSL_ENCODER_INSTANCE_get_encoder_ctx(OSSL_ENCODER_INSTANCE *encoder_inst)
245 {
246     if (encoder_inst == NULL)
247         return NULL;
248     return encoder_inst->encoderctx;
249 }
250
251 const char *
252 OSSL_ENCODER_INSTANCE_get_input_type(OSSL_ENCODER_INSTANCE *encoder_inst)
253 {
254     if (encoder_inst == NULL)
255         return NULL;
256     return encoder_inst->input_type;
257 }
258
259 const char *
260 OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst)
261 {
262     if (encoder_inst == NULL)
263         return NULL;
264     return encoder_inst->output_type;
265 }
266
267 static int encoder_process(OSSL_ENCODER_CTX *ctx, BIO *out)
268 {
269     size_t i, end;
270     void *latest_output = NULL;
271     size_t latest_output_length = 0;
272     const char *latest_output_type = NULL;
273     const char *last_input_type = NULL;
274     int ok = 0;
275
276     end = OSSL_ENCODER_CTX_get_num_encoders(ctx);
277     for (i = 0; i < end; i++) {
278         OSSL_ENCODER_INSTANCE *encoder_inst =
279             sk_OSSL_ENCODER_INSTANCE_value(ctx->encoder_insts, i);
280         OSSL_ENCODER *encoder = OSSL_ENCODER_INSTANCE_get_encoder(encoder_inst);
281         void *encoderctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(encoder_inst);
282         const char *current_input_type =
283             OSSL_ENCODER_INSTANCE_get_input_type(encoder_inst);
284         const char *current_output_type =
285             OSSL_ENCODER_INSTANCE_get_output_type(encoder_inst);
286         BIO *current_out;
287         BIO *allocated_out = NULL;
288         const void *current_data = NULL;
289         OSSL_PARAM abstract[3];
290         OSSL_PARAM *abstract_p;
291         const OSSL_PARAM *current_abstract = NULL;
292
293         if (latest_output_type == NULL) {
294             /*
295              * This is the first iteration, so we prepare the object to be
296              * encoded
297              */
298
299             current_data = ctx->construct(encoder_inst, ctx->construct_data);
300
301             /* Assume that the constructor recorded an error */
302             if (current_data == NULL)
303                 goto loop_end;
304         } else {
305             /*
306              * Check that the latest output type matches the currently
307              * considered encoder
308              */
309             if (!OSSL_ENCODER_is_a(encoder, latest_output_type))
310                 continue;
311
312             /*
313              * If there is a latest output type, there should be a latest output
314              */
315             if (!ossl_assert(latest_output != NULL)) {
316                 ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR);
317                 goto loop_end;
318             }
319
320             /*
321              * Create an object abstraction from the latest output, which was
322              * stolen from the previous round.
323              */
324             abstract_p = abstract;
325             if (last_input_type != NULL)
326                 *abstract_p++ =
327                     OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
328                                                      (char *)last_input_type, 0);
329             *abstract_p++ =
330                 OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
331                                                   latest_output,
332                                                   latest_output_length);
333             *abstract_p = OSSL_PARAM_construct_end();
334             current_abstract = abstract;
335         }
336
337         /*
338          * If the desired output type matches the output type of the currently
339          * considered encoder, we're setting up final output.  Otherwise, set
340          * up an intermediary memory output.
341          */
342         if (strcasecmp(ctx->output_type, current_output_type) == 0)
343             current_out = out;
344         else if ((current_out = allocated_out = BIO_new(BIO_s_mem())) == NULL)
345             goto loop_end;     /* Assume BIO_new() recorded an error */
346
347         ok = encoder->encode(encoderctx, (OSSL_CORE_BIO *)current_out,
348                              current_data, current_abstract, ctx->selection,
349                              ossl_pw_passphrase_callback_enc, &ctx->pwdata);
350
351         if (current_input_type != NULL)
352             last_input_type = current_input_type;
353
354         if (!ok)
355             goto loop_end;
356
357         OPENSSL_free(latest_output);
358
359         /*
360          * Steal the output from the BIO_s_mem, if we did allocate one.
361          * That'll be the data for an object abstraction in the next round.
362          */
363         if (allocated_out != NULL) {
364             BUF_MEM *buf;
365
366             BIO_get_mem_ptr(allocated_out, &buf);
367             latest_output = buf->data;
368             latest_output_length = buf->length;
369             memset(buf, 0, sizeof(*buf));
370             BIO_free(allocated_out);
371         }
372
373      loop_end:
374         if (current_data != NULL)
375             ctx->cleanup(ctx->construct_data);
376
377         if (ok)
378             break;
379     }
380
381     OPENSSL_free(latest_output);
382     return ok;
383 }