| /* |
| * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved. |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| #include <ctype.h> |
| |
| #include <openssl/core_names.h> |
| #include <openssl/bio.h> |
| #include <openssl/encoder.h> |
| #include <openssl/buffer.h> |
| #include <openssl/params.h> |
| #include <openssl/provider.h> |
| #include <openssl/trace.h> |
| #include <crypto/bn.h> |
| #include "internal/bio.h" |
| #include "internal/ffc.h" |
| #include "internal/provider.h" |
| #include "internal/encoder.h" |
| #include "encoder_local.h" |
| |
| /* Number of octets per line */ |
| #define LABELED_BUF_PRINT_WIDTH 16 |
| #define LABELED_BN_PRINT_WIDTH 16 |
| |
| #ifdef SIXTY_FOUR_BIT_LONG |
| #define BN_FMTu "%lu" |
| #define BN_FMTx "%lx" |
| #endif |
| |
| #ifdef SIXTY_FOUR_BIT |
| #define BN_FMTu "%llu" |
| #define BN_FMTx "%llx" |
| #endif |
| |
| #ifdef THIRTY_TWO_BIT |
| #define BN_FMTu "%u" |
| #define BN_FMTx "%x" |
| #endif |
| |
| struct encoder_process_data_st { |
| OSSL_ENCODER_CTX *ctx; |
| |
| /* Current BIO */ |
| BIO *bio; |
| |
| /* Index of the current encoder instance to be processed */ |
| int current_encoder_inst_index; |
| |
| /* Processing data passed down through recursion */ |
| int level; /* Recursion level */ |
| OSSL_ENCODER_INSTANCE *next_encoder_inst; |
| int count_output_structure; |
| |
| /* Processing data passed up through recursion */ |
| OSSL_ENCODER_INSTANCE *prev_encoder_inst; |
| unsigned char *running_output; |
| size_t running_output_length; |
| /* Data type = the name of the first succeeding encoder implementation */ |
| const char *data_type; |
| }; |
| |
| static int encoder_process(struct encoder_process_data_st *data); |
| |
| int OSSL_ENCODER_to_bio(OSSL_ENCODER_CTX *ctx, BIO *out) |
| { |
| struct encoder_process_data_st data; |
| |
| memset(&data, 0, sizeof(data)); |
| data.ctx = ctx; |
| data.bio = out; |
| data.current_encoder_inst_index = OSSL_ENCODER_CTX_get_num_encoders(ctx); |
| |
| if (data.current_encoder_inst_index == 0) { |
| ERR_raise_data(ERR_LIB_OSSL_ENCODER, OSSL_ENCODER_R_ENCODER_NOT_FOUND, |
| "No encoders were found. For standard encoders you need " |
| "at least one of the default or base providers " |
| "available. Did you forget to load them?"); |
| return 0; |
| } |
| |
| if (ctx->cleanup == NULL || ctx->construct == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INIT_FAIL); |
| return 0; |
| } |
| |
| return encoder_process(&data) > 0; |
| } |
| |
| #ifndef OPENSSL_NO_STDIO |
| static BIO *bio_from_file(FILE *fp) |
| { |
| BIO *b; |
| |
| if ((b = BIO_new(BIO_s_file())) == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_BUF_LIB); |
| return NULL; |
| } |
| BIO_set_fp(b, fp, BIO_NOCLOSE); |
| return b; |
| } |
| |
| int OSSL_ENCODER_to_fp(OSSL_ENCODER_CTX *ctx, FILE *fp) |
| { |
| BIO *b = bio_from_file(fp); |
| int ret = 0; |
| |
| if (b != NULL) |
| ret = OSSL_ENCODER_to_bio(ctx, b); |
| |
| BIO_free(b); |
| return ret; |
| } |
| #endif |
| |
| int OSSL_ENCODER_to_data(OSSL_ENCODER_CTX *ctx, unsigned char **pdata, |
| size_t *pdata_len) |
| { |
| BIO *out; |
| BUF_MEM *buf = NULL; |
| int ret = 0; |
| |
| if (pdata_len == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| out = BIO_new(BIO_s_mem()); |
| |
| if (out != NULL |
| && OSSL_ENCODER_to_bio(ctx, out) |
| && BIO_get_mem_ptr(out, &buf) > 0) { |
| ret = 1; /* Hope for the best. A too small buffer will clear this */ |
| |
| if (pdata != NULL && *pdata != NULL) { |
| if (*pdata_len < buf->length) |
| /* |
| * It's tempting to do |*pdata_len = (size_t)buf->length| |
| * However, it's believed to be confusing more than helpful, |
| * so we don't. |
| */ |
| ret = 0; |
| else |
| *pdata_len -= buf->length; |
| } else { |
| /* The buffer with the right size is already allocated for us */ |
| *pdata_len = (size_t)buf->length; |
| } |
| |
| if (ret) { |
| if (pdata != NULL) { |
| if (*pdata != NULL) { |
| memcpy(*pdata, buf->data, buf->length); |
| *pdata += buf->length; |
| } else { |
| /* In this case, we steal the data from BIO_s_mem() */ |
| *pdata = (unsigned char *)buf->data; |
| buf->data = NULL; |
| } |
| } |
| } |
| } |
| BIO_free(out); |
| return ret; |
| } |
| |
| int OSSL_ENCODER_CTX_set_selection(OSSL_ENCODER_CTX *ctx, int selection) |
| { |
| if (ctx == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| if (selection == 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_INVALID_ARGUMENT); |
| return 0; |
| } |
| |
| ctx->selection = selection; |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_set_output_type(OSSL_ENCODER_CTX *ctx, |
| const char *output_type) |
| { |
| if (ctx == NULL || output_type == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| ctx->output_type = output_type; |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_set_output_structure(OSSL_ENCODER_CTX *ctx, |
| const char *output_structure) |
| { |
| if (ctx == NULL || output_structure == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| ctx->output_structure = output_structure; |
| return 1; |
| } |
| |
| static OSSL_ENCODER_INSTANCE *ossl_encoder_instance_new(OSSL_ENCODER *encoder, |
| void *encoderctx) |
| { |
| OSSL_ENCODER_INSTANCE *encoder_inst = NULL; |
| const OSSL_PROVIDER *prov; |
| OSSL_LIB_CTX *libctx; |
| const OSSL_PROPERTY_LIST *props; |
| const OSSL_PROPERTY_DEFINITION *prop; |
| |
| if (encoder == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if ((encoder_inst = OPENSSL_zalloc(sizeof(*encoder_inst))) == NULL) |
| return 0; |
| |
| if (!OSSL_ENCODER_up_ref(encoder)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR); |
| goto err; |
| } |
| |
| prov = OSSL_ENCODER_get0_provider(encoder); |
| libctx = ossl_provider_libctx(prov); |
| props = ossl_encoder_parsed_properties(encoder); |
| if (props == NULL) { |
| ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INVALID_PROPERTY_DEFINITION, |
| "there are no property definitions with encoder %s", |
| OSSL_ENCODER_get0_name(encoder)); |
| goto err; |
| } |
| |
| /* The "output" property is mandatory */ |
| prop = ossl_property_find_property(props, libctx, "output"); |
| encoder_inst->output_type = ossl_property_get_string_value(libctx, prop); |
| if (encoder_inst->output_type == NULL) { |
| ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INVALID_PROPERTY_DEFINITION, |
| "the mandatory 'output' property is missing " |
| "for encoder %s (properties: %s)", |
| OSSL_ENCODER_get0_name(encoder), |
| OSSL_ENCODER_get0_properties(encoder)); |
| goto err; |
| } |
| |
| /* The "structure" property is optional */ |
| prop = ossl_property_find_property(props, libctx, "structure"); |
| if (prop != NULL) |
| encoder_inst->output_structure |
| = ossl_property_get_string_value(libctx, prop); |
| |
| encoder_inst->encoder = encoder; |
| encoder_inst->encoderctx = encoderctx; |
| return encoder_inst; |
| err: |
| ossl_encoder_instance_free(encoder_inst); |
| return NULL; |
| } |
| |
| void ossl_encoder_instance_free(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst != NULL) { |
| if (encoder_inst->encoder != NULL) |
| encoder_inst->encoder->freectx(encoder_inst->encoderctx); |
| encoder_inst->encoderctx = NULL; |
| OSSL_ENCODER_free(encoder_inst->encoder); |
| encoder_inst->encoder = NULL; |
| OPENSSL_free(encoder_inst); |
| } |
| } |
| |
| static int ossl_encoder_ctx_add_encoder_inst(OSSL_ENCODER_CTX *ctx, |
| OSSL_ENCODER_INSTANCE *ei) |
| { |
| int ok; |
| |
| if (ctx->encoder_insts == NULL |
| && (ctx->encoder_insts = sk_OSSL_ENCODER_INSTANCE_new_null()) == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_CRYPTO_LIB); |
| return 0; |
| } |
| |
| ok = (sk_OSSL_ENCODER_INSTANCE_push(ctx->encoder_insts, ei) > 0); |
| if (ok) { |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "(ctx %p) Added encoder instance %p (encoder %p):\n" |
| " %s with %s\n", |
| (void *)ctx, (void *)ei, (void *)ei->encoder, |
| OSSL_ENCODER_get0_name(ei->encoder), |
| OSSL_ENCODER_get0_properties(ei->encoder)); |
| } |
| OSSL_TRACE_END(ENCODER); |
| } |
| return ok; |
| } |
| |
| int OSSL_ENCODER_CTX_add_encoder(OSSL_ENCODER_CTX *ctx, OSSL_ENCODER *encoder) |
| { |
| OSSL_ENCODER_INSTANCE *encoder_inst = NULL; |
| const OSSL_PROVIDER *prov = NULL; |
| void *encoderctx = NULL; |
| void *provctx = NULL; |
| |
| if (ctx == NULL || encoder == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| prov = OSSL_ENCODER_get0_provider(encoder); |
| provctx = OSSL_PROVIDER_get0_provider_ctx(prov); |
| |
| if ((encoderctx = encoder->newctx(provctx)) == NULL |
| || (encoder_inst = ossl_encoder_instance_new(encoder, encoderctx)) == NULL) |
| goto err; |
| /* Avoid double free of encoderctx on further errors */ |
| encoderctx = NULL; |
| |
| if (!ossl_encoder_ctx_add_encoder_inst(ctx, encoder_inst)) |
| goto err; |
| |
| return 1; |
| err: |
| ossl_encoder_instance_free(encoder_inst); |
| if (encoderctx != NULL) |
| encoder->freectx(encoderctx); |
| return 0; |
| } |
| |
| int OSSL_ENCODER_CTX_add_extra(OSSL_ENCODER_CTX *ctx, |
| OSSL_LIB_CTX *libctx, const char *propq) |
| { |
| if (ctx == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_get_num_encoders(OSSL_ENCODER_CTX *ctx) |
| { |
| if (ctx == NULL || ctx->encoder_insts == NULL) |
| return 0; |
| return sk_OSSL_ENCODER_INSTANCE_num(ctx->encoder_insts); |
| } |
| |
| int OSSL_ENCODER_CTX_set_construct(OSSL_ENCODER_CTX *ctx, |
| OSSL_ENCODER_CONSTRUCT *construct) |
| { |
| if (ctx == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| ctx->construct = construct; |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_set_construct_data(OSSL_ENCODER_CTX *ctx, |
| void *construct_data) |
| { |
| if (ctx == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| ctx->construct_data = construct_data; |
| return 1; |
| } |
| |
| int OSSL_ENCODER_CTX_set_cleanup(OSSL_ENCODER_CTX *ctx, |
| OSSL_ENCODER_CLEANUP *cleanup) |
| { |
| if (ctx == NULL) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER); |
| return 0; |
| } |
| |
| if (ctx->frozen != 0) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); |
| return 0; |
| } |
| |
| ctx->cleanup = cleanup; |
| return 1; |
| } |
| |
| OSSL_ENCODER * |
| OSSL_ENCODER_INSTANCE_get_encoder(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->encoder; |
| } |
| |
| void * |
| OSSL_ENCODER_INSTANCE_get_encoder_ctx(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->encoderctx; |
| } |
| |
| const char * |
| OSSL_ENCODER_INSTANCE_get_output_type(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->output_type; |
| } |
| |
| const char * |
| OSSL_ENCODER_INSTANCE_get_output_structure(OSSL_ENCODER_INSTANCE *encoder_inst) |
| { |
| if (encoder_inst == NULL) |
| return NULL; |
| return encoder_inst->output_structure; |
| } |
| |
| static int encoder_process(struct encoder_process_data_st *data) |
| { |
| OSSL_ENCODER_INSTANCE *current_encoder_inst = NULL; |
| OSSL_ENCODER *current_encoder = NULL; |
| OSSL_ENCODER_CTX *current_encoder_ctx = NULL; |
| BIO *allocated_out = NULL; |
| const void *original_data = NULL; |
| OSSL_PARAM abstract[10]; |
| const OSSL_PARAM *current_abstract = NULL; |
| int i; |
| int ok = -1; /* -1 signifies that the lookup loop gave nothing */ |
| int top = 0; |
| |
| if (data->next_encoder_inst == NULL) { |
| /* First iteration, where we prepare for what is to come */ |
| |
| data->count_output_structure = data->ctx->output_structure == NULL ? -1 : 0; |
| top = 1; |
| } |
| |
| for (i = data->current_encoder_inst_index; i-- > 0;) { |
| OSSL_ENCODER *next_encoder = NULL; |
| const char *current_output_type; |
| const char *current_output_structure; |
| struct encoder_process_data_st new_data; |
| |
| if (!top) |
| next_encoder = OSSL_ENCODER_INSTANCE_get_encoder(data->next_encoder_inst); |
| |
| current_encoder_inst = sk_OSSL_ENCODER_INSTANCE_value(data->ctx->encoder_insts, i); |
| current_encoder = OSSL_ENCODER_INSTANCE_get_encoder(current_encoder_inst); |
| current_encoder_ctx = OSSL_ENCODER_INSTANCE_get_encoder_ctx(current_encoder_inst); |
| current_output_type = OSSL_ENCODER_INSTANCE_get_output_type(current_encoder_inst); |
| current_output_structure = OSSL_ENCODER_INSTANCE_get_output_structure(current_encoder_inst); |
| memset(&new_data, 0, sizeof(new_data)); |
| new_data.ctx = data->ctx; |
| new_data.current_encoder_inst_index = i; |
| new_data.next_encoder_inst = current_encoder_inst; |
| new_data.count_output_structure = data->count_output_structure; |
| new_data.level = data->level + 1; |
| |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "[%d] (ctx %p) Considering encoder instance %p (encoder %p)\n", |
| data->level, (void *)data->ctx, |
| (void *)current_encoder_inst, (void *)current_encoder); |
| } |
| OSSL_TRACE_END(ENCODER); |
| |
| /* |
| * If this is the top call, we check if the output type of the current |
| * encoder matches the desired output type. |
| * If this isn't the top call, i.e. this is deeper in the recursion, |
| * we instead check if the output type of the current encoder matches |
| * the name of the next encoder (the one found by the parent call). |
| */ |
| if (top) { |
| if (data->ctx->output_type != NULL |
| && OPENSSL_strcasecmp(current_output_type, |
| data->ctx->output_type) |
| != 0) { |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "[%d] Skipping because current encoder output type (%s) != desired output type (%s)\n", |
| data->level, |
| current_output_type, data->ctx->output_type); |
| } |
| OSSL_TRACE_END(ENCODER); |
| continue; |
| } |
| } else { |
| if (!OSSL_ENCODER_is_a(next_encoder, current_output_type)) { |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "[%d] Skipping because current encoder output type (%s) != name of encoder %p\n", |
| data->level, |
| current_output_type, (void *)next_encoder); |
| } |
| OSSL_TRACE_END(ENCODER); |
| continue; |
| } |
| } |
| |
| /* |
| * If the caller and the current encoder specify an output structure, |
| * Check if they match. If they do, count the match, otherwise skip |
| * the current encoder. |
| */ |
| if (data->ctx->output_structure != NULL |
| && current_output_structure != NULL) { |
| if (OPENSSL_strcasecmp(data->ctx->output_structure, |
| current_output_structure) |
| != 0) { |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "[%d] Skipping because current encoder output structure (%s) != ctx output structure (%s)\n", |
| data->level, |
| current_output_structure, |
| data->ctx->output_structure); |
| } |
| OSSL_TRACE_END(ENCODER); |
| continue; |
| } |
| |
| data->count_output_structure++; |
| } |
| |
| /* |
| * Recurse to process the encoder implementations before the current |
| * one. |
| */ |
| ok = encoder_process(&new_data); |
| |
| data->prev_encoder_inst = new_data.prev_encoder_inst; |
| data->running_output = new_data.running_output; |
| data->running_output_length = new_data.running_output_length; |
| |
| /* |
| * ok == -1 means that the recursion call above gave no further |
| * encoders, and that the one we're currently at should |
| * be tried. |
| * ok == 0 means that something failed in the recursion call |
| * above, making the result unsuitable for a chain. |
| * In this case, we simply continue to try finding a |
| * suitable encoder at this recursion level. |
| * ok == 1 means that the recursion call was successful, and we |
| * try to use the result at this recursion level. |
| */ |
| if (ok != 0) |
| break; |
| |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "[%d] Skipping because recursion level %d failed\n", |
| data->level, new_data.level); |
| } |
| OSSL_TRACE_END(ENCODER); |
| } |
| |
| /* |
| * If |i < 0|, we didn't find any useful encoder in this recursion, so |
| * we do the rest of the process only if |i >= 0|. |
| */ |
| if (i < 0) { |
| ok = -1; |
| |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "[%d] (ctx %p) No suitable encoder found\n", |
| data->level, (void *)data->ctx); |
| } |
| OSSL_TRACE_END(ENCODER); |
| } else { |
| /* Preparations */ |
| |
| switch (ok) { |
| case 0: |
| break; |
| case -1: |
| /* |
| * We have reached the beginning of the encoder instance sequence, |
| * so we prepare the object to be encoded. |
| */ |
| |
| /* |
| * |data->count_output_structure| is one of these values: |
| * |
| * -1 There is no desired output structure |
| * 0 There is a desired output structure, and it wasn't |
| * matched by any of the encoder instances that were |
| * considered |
| * >0 There is a desired output structure, and at least one |
| * of the encoder instances matched it |
| */ |
| if (data->count_output_structure == 0) |
| return 0; |
| |
| original_data = data->ctx->construct(current_encoder_inst, |
| data->ctx->construct_data); |
| |
| /* Also set the data type, using the encoder implementation name */ |
| data->data_type = OSSL_ENCODER_get0_name(current_encoder); |
| |
| /* Assume that the constructor recorded an error */ |
| if (original_data != NULL) |
| ok = 1; |
| else |
| ok = 0; |
| break; |
| case 1: |
| if (!ossl_assert(data->running_output != NULL)) { |
| ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR); |
| ok = 0; |
| break; |
| } |
| |
| { |
| /* |
| * Create an object abstraction from the latest output, which |
| * was stolen from the previous round. |
| */ |
| |
| OSSL_PARAM *abstract_p = abstract; |
| const char *prev_output_structure = OSSL_ENCODER_INSTANCE_get_output_structure(data->prev_encoder_inst); |
| |
| *abstract_p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, |
| (char *)data->data_type, 0); |
| if (prev_output_structure != NULL) |
| *abstract_p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, |
| (char *)prev_output_structure, |
| 0); |
| *abstract_p++ = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, |
| data->running_output, |
| data->running_output_length); |
| *abstract_p = OSSL_PARAM_construct_end(); |
| current_abstract = abstract; |
| } |
| break; |
| } |
| |
| /* Calling the encoder implementation */ |
| |
| if (ok) { |
| OSSL_CORE_BIO *cbio = NULL; |
| BIO *current_out = NULL; |
| |
| /* |
| * If we're at the last encoder instance to use, we're setting up |
| * final output. Otherwise, set up an intermediary memory output. |
| */ |
| if (top) |
| current_out = data->bio; |
| else if ((current_out = allocated_out = BIO_new(BIO_s_mem())) |
| == NULL) |
| ok = 0; /* Assume BIO_new() recorded an error */ |
| |
| if (ok) |
| ok = (cbio = ossl_core_bio_new_from_bio(current_out)) != NULL; |
| if (ok) { |
| ok = current_encoder->encode(current_encoder_ctx, cbio, |
| original_data, current_abstract, |
| data->ctx->selection, |
| ossl_pw_passphrase_callback_enc, |
| &data->ctx->pwdata); |
| OSSL_TRACE_BEGIN(ENCODER) |
| { |
| BIO_printf(trc_out, |
| "[%d] (ctx %p) Running encoder instance %p => %d\n", |
| data->level, (void *)data->ctx, |
| (void *)current_encoder_inst, ok); |
| } |
| OSSL_TRACE_END(ENCODER); |
| } |
| |
| ossl_core_bio_free(cbio); |
| data->prev_encoder_inst = current_encoder_inst; |
| } |
| } |
| |
| /* Cleanup and collecting the result */ |
| |
| OPENSSL_free(data->running_output); |
| data->running_output = NULL; |
| |
| /* |
| * Steal the output from the BIO_s_mem, if we did allocate one. |
| * That'll be the data for an object abstraction in the next round. |
| */ |
| if (allocated_out != NULL) { |
| BUF_MEM *buf; |
| |
| BIO_get_mem_ptr(allocated_out, &buf); |
| data->running_output = (unsigned char *)buf->data; |
| data->running_output_length = buf->length; |
| memset(buf, 0, sizeof(*buf)); |
| } |
| |
| BIO_free(allocated_out); |
| if (original_data != NULL) |
| data->ctx->cleanup(data->ctx->construct_data); |
| return ok; |
| } |
| |
| int ossl_bio_print_labeled_bignum(BIO *out, const char *label, const BIGNUM *bn) |
| { |
| int ret = 0, use_sep = 0; |
| char *hex_str = NULL, *p; |
| const char spaces[] = " "; |
| const char *post_label_spc = " "; |
| |
| const char *neg = ""; |
| int bytes; |
| |
| if (bn == NULL) |
| return 0; |
| if (label == NULL) { |
| label = ""; |
| post_label_spc = ""; |
| } |
| |
| if (BN_is_zero(bn)) |
| return BIO_printf(out, "%s%s0\n", label, post_label_spc); |
| |
| if (BN_num_bytes(bn) <= BN_BYTES) { |
| BN_ULONG *words = bn_get_words(bn); |
| |
| if (BN_is_negative(bn)) |
| neg = "-"; |
| |
| return BIO_printf(out, "%s%s%s" BN_FMTu " (%s0x" BN_FMTx ")\n", |
| label, post_label_spc, neg, words[0], neg, words[0]); |
| } |
| |
| hex_str = BN_bn2hex(bn); |
| if (hex_str == NULL) |
| return 0; |
| |
| p = hex_str; |
| if (*p == '-') { |
| ++p; |
| neg = " (Negative)"; |
| } |
| if (BIO_printf(out, "%s%s\n", label, neg) <= 0) |
| goto err; |
| |
| /* Keep track of how many bytes we have printed out so far */ |
| bytes = 0; |
| |
| if (BIO_printf(out, "%s", spaces) <= 0) |
| goto err; |
| |
| while (*p != '\0') { |
| /* Do a newline after every n hex bytes + add the space indent */ |
| if ((bytes % LABELED_BN_PRINT_WIDTH) == 0 && bytes > 0) { |
| if (BIO_printf(out, ":\n%s", spaces) <= 0) |
| goto err; |
| use_sep = 0; /* The first byte on the next line doesn't have a : */ |
| } |
| if (BIO_printf(out, "%s%c%c", use_sep ? ":" : "", |
| tolower((unsigned char)p[0]), |
| tolower((unsigned char)p[1])) |
| <= 0) |
| goto err; |
| ++bytes; |
| p += 2; |
| use_sep = 1; |
| } |
| if (BIO_printf(out, "\n") <= 0) |
| goto err; |
| ret = 1; |
| err: |
| OPENSSL_free(hex_str); |
| return ret; |
| } |
| |
| int ossl_bio_print_labeled_buf(BIO *out, const char *label, |
| const unsigned char *buf, size_t buflen) |
| { |
| size_t i; |
| |
| if (BIO_printf(out, "%s\n", label) <= 0) |
| return 0; |
| |
| for (i = 0; i < buflen; i++) { |
| if ((i % LABELED_BUF_PRINT_WIDTH) == 0) { |
| if (i > 0 && BIO_printf(out, "\n") <= 0) |
| return 0; |
| if (BIO_printf(out, " ") <= 0) |
| return 0; |
| } |
| |
| if (BIO_printf(out, "%02x%s", buf[i], |
| (i == buflen - 1) ? "" : ":") |
| <= 0) |
| return 0; |
| } |
| if (BIO_printf(out, "\n") <= 0) |
| return 0; |
| |
| return 1; |
| } |
| |
| #if !defined(OPENSSL_NO_DH) || !defined(OPENSSL_NO_DSA) |
| int ossl_bio_print_ffc_params(BIO *out, const FFC_PARAMS *ffc) |
| { |
| if (ffc->nid != NID_undef) { |
| #ifndef OPENSSL_NO_DH |
| const DH_NAMED_GROUP *group = ossl_ffc_uid_to_dh_named_group(ffc->nid); |
| const char *name = ossl_ffc_named_group_get_name(group); |
| |
| if (name == NULL) |
| goto err; |
| if (BIO_printf(out, "GROUP: %s\n", name) <= 0) |
| goto err; |
| return 1; |
| #else |
| /* How could this be? We should not have a nid in a no-dh build. */ |
| goto err; |
| #endif |
| } |
| |
| if (!ossl_bio_print_labeled_bignum(out, "P: ", ffc->p)) |
| goto err; |
| if (ffc->q != NULL) { |
| if (!ossl_bio_print_labeled_bignum(out, "Q: ", ffc->q)) |
| goto err; |
| } |
| if (!ossl_bio_print_labeled_bignum(out, "G: ", ffc->g)) |
| goto err; |
| if (ffc->j != NULL) { |
| if (!ossl_bio_print_labeled_bignum(out, "J: ", ffc->j)) |
| goto err; |
| } |
| if (ffc->seed != NULL) { |
| if (!ossl_bio_print_labeled_buf(out, "SEED:", ffc->seed, ffc->seedlen)) |
| goto err; |
| } |
| if (ffc->gindex != -1) { |
| if (BIO_printf(out, "gindex: %d\n", ffc->gindex) <= 0) |
| goto err; |
| } |
| if (ffc->pcounter != -1) { |
| if (BIO_printf(out, "pcounter: %d\n", ffc->pcounter) <= 0) |
| goto err; |
| } |
| if (ffc->h != 0) { |
| if (BIO_printf(out, "h: %d\n", ffc->h) <= 0) |
| goto err; |
| } |
| return 1; |
| err: |
| return 0; |
| } |
| |
| #endif |