/* * Copyright 2020-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 */ {- use OpenSSL::paramnames qw(produce_param_decoder); -} /* * This is a decoder that's completely internal to the 'file:' store * implementation. Only code in file_store.c know about this one. Because * of this close relationship, we can cut certain corners, such as making * assumptions about the "provider context", which is currently simply the * provider context that the file_store.c code operates within. * * All this does is to read known binary encodings (currently: DER, MSBLOB, * PVK) from the input if it can, and passes it on to the data callback as * an object abstraction, leaving it to the callback to figure out what it * actually is. * * This MUST be made the last decoder in a chain, leaving it to other more * specialized decoders to recognise and process their stuff first. */ #include #include #include #include #include #include #include #include #include "internal/cryptlib.h" #include #include "internal/asn1.h" #include "internal/sizes.h" #include "crypto/pem.h" /* For internal PVK and "blob" headers */ #include "prov/bio.h" #include "prov/file_store_local.h" /* * newctx and freectx are not strictly necessary. However, the method creator, * ossl_decoder_from_algorithm(), demands that they exist, so we make sure to * oblige. */ static OSSL_FUNC_decoder_newctx_fn any2obj_newctx; static OSSL_FUNC_decoder_freectx_fn any2obj_freectx; struct any2obj_ctx_st { PROV_CTX *provctx; char data_structure[OSSL_MAX_CODEC_STRUCT_SIZE]; }; static void *any2obj_newctx(void *provctx) { struct any2obj_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx)); if (ctx != NULL) ctx->provctx = provctx; return ctx; } static void any2obj_freectx(void *ctx) { OPENSSL_free(ctx); } {- produce_param_decoder('any2obj_set_ctx_params', (['OBJECT_PARAM_DATA_STRUCTURE', 'datastruct', 'utf8_string'], )); -} static int any2obj_set_ctx_params(void *vctx, const OSSL_PARAM params[]) { struct any2obj_ctx_st *ctx = vctx; struct any2obj_set_ctx_params_st p; char *str; if (ctx == NULL || !any2obj_set_ctx_params_decoder(params, &p)) return 0; str = ctx->data_structure; if (p.datastruct != NULL && !OSSL_PARAM_get_utf8_string(p.datastruct, &str, sizeof(ctx->data_structure))) return 0; return 1; } static const OSSL_PARAM *any2obj_settable_ctx_params(ossl_unused void *provctx) { return any2obj_set_ctx_params_list; } static int any2obj_decode_final(void *vctx, int objtype, const char *input_type, const char *data_type, BUF_MEM *mem, OSSL_CALLBACK *data_cb, void *data_cbarg) { struct any2obj_ctx_st *ctx = vctx; /* * 1 indicates that we successfully decoded something, or not at all. * Ending up "empty handed" is not an error. */ int ok = 1; if (mem != NULL) { OSSL_PARAM params[6], *p = params; if (data_type != NULL) *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE, (char *)data_type, 0); if (input_type != NULL) *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_INPUT_TYPE, (char *)input_type, 0); if (*ctx->data_structure != '\0') *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, (char *)ctx->data_structure, 0); *p++ = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype); *p++ = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, mem->data, mem->length); *p = OSSL_PARAM_construct_end(); ok = data_cb(params, data_cbarg); BUF_MEM_free(mem); } return ok; } static OSSL_FUNC_decoder_decode_fn der2obj_decode; static int der2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, OSSL_CALLBACK *data_cb, void *data_cbarg, OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) { struct any2obj_ctx_st *ctx = vctx; BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); BUF_MEM *mem = NULL; int ok; if (in == NULL) return 0; ERR_set_mark(); ok = (asn1_d2i_read_bio(in, &mem) >= 0); ERR_pop_to_mark(); if (!ok && mem != NULL) { BUF_MEM_free(mem); mem = NULL; } BIO_free(in); /* any2obj_decode_final() frees |mem| for us */ return any2obj_decode_final(ctx, OSSL_OBJECT_UNKNOWN, NULL, NULL, mem, data_cb, data_cbarg); } static OSSL_FUNC_decoder_decode_fn msblob2obj_decode; static int msblob2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, OSSL_CALLBACK *data_cb, void *data_cbarg, OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) { struct any2obj_ctx_st *ctx = vctx; BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); BUF_MEM *mem = NULL; size_t mem_len = 0, mem_want; const unsigned char *p; unsigned int bitlen, magic; int isdss = -1; int ispub = -1; int ok = 0; if (in == NULL) goto err; mem_want = 16; /* The size of the MSBLOB header */ if ((mem = BUF_MEM_new()) == NULL || !BUF_MEM_grow(mem, mem_want)) { ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); goto err; } ERR_set_mark(); ok = BIO_read(in, &mem->data[0], (int)mem_want) == (int)mem_want; mem_len += mem_want; ERR_pop_to_mark(); if (!ok) goto next; ERR_set_mark(); p = (unsigned char *)&mem->data[0]; ok = ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) > 0; ERR_pop_to_mark(); if (!ok) goto next; ok = 0; mem_want = ossl_blob_length(bitlen, isdss, ispub); if (!BUF_MEM_grow(mem, mem_len + mem_want)) { ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); goto err; } ERR_set_mark(); ok = BIO_read(in, &mem->data[mem_len], (int)mem_want) == (int)mem_want; mem_len += mem_want; ERR_pop_to_mark(); next: /* Free resources we no longer need. */ BIO_free(in); if (!ok && mem != NULL) { BUF_MEM_free(mem); mem = NULL; } /* any2obj_decode_final() frees |mem| for us */ return any2obj_decode_final(ctx, OSSL_OBJECT_PKEY, "msblob", isdss ? "DSA" : "RSA", mem, data_cb, data_cbarg); err: BIO_free(in); BUF_MEM_free(mem); return 0; } static OSSL_FUNC_decoder_decode_fn pvk2obj_decode; static int pvk2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection, OSSL_CALLBACK *data_cb, void *data_cbarg, OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg) { struct any2obj_ctx_st *ctx = vctx; BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin); BUF_MEM *mem = NULL; size_t mem_len = 0, mem_want; const unsigned char *p; unsigned int saltlen, keylen; int ok = 0, isdss = -1; if (in == NULL) goto err; mem_want = 24; /* The size of the PVK header */ if ((mem = BUF_MEM_new()) == NULL || !BUF_MEM_grow(mem, mem_want)) { ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); goto err; } ERR_set_mark(); ok = BIO_read(in, &mem->data[0], (int)mem_want) == (int)mem_want; mem_len += mem_want; ERR_pop_to_mark(); if (!ok) goto next; ERR_set_mark(); p = (unsigned char *)&mem->data[0]; ok = ossl_do_PVK_header(&p, 24, 0, &isdss, &saltlen, &keylen) > 0; ERR_pop_to_mark(); if (!ok) goto next; ok = 0; mem_want = saltlen + keylen; if (!BUF_MEM_grow(mem, mem_len + mem_want)) { ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB); goto err; } ERR_set_mark(); ok = BIO_read(in, &mem->data[mem_len], (int)mem_want) == (int)mem_want; mem_len += mem_want; ERR_pop_to_mark(); next: /* Free resources we no longer need. */ BIO_free(in); if (!ok && mem != NULL) { BUF_MEM_free(mem); mem = NULL; } /* any2obj_decode_final() frees |mem| for us */ return any2obj_decode_final(ctx, OSSL_OBJECT_PKEY, "pvk", ok ? (isdss ? "DSA" : "RSA") : NULL, mem, data_cb, data_cbarg); err: BIO_free(in); BUF_MEM_free(mem); return 0; } #define MAKE_DECODER(fromtype, objtype) \ static const OSSL_DISPATCH fromtype##_to_obj_decoder_functions[] = { \ { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))any2obj_newctx }, \ { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))any2obj_freectx }, \ { OSSL_FUNC_DECODER_DECODE, (void (*)(void))fromtype##2obj_decode }, \ { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, \ (void (*)(void))any2obj_settable_ctx_params }, \ { OSSL_FUNC_DECODER_SET_CTX_PARAMS, \ (void (*)(void))any2obj_set_ctx_params }, \ OSSL_DISPATCH_END \ } MAKE_DECODER(der, OSSL_OBJECT_UNKNOWN); MAKE_DECODER(msblob, OSSL_OBJECT_PKEY); MAKE_DECODER(pvk, OSSL_OBJECT_PKEY); const OSSL_ALGORITHM ossl_any_to_obj_algorithm[] = { { "obj", "input=DER", der_to_obj_decoder_functions }, { "obj", "input=MSBLOB", msblob_to_obj_decoder_functions }, { "obj", "input=PVK", pvk_to_obj_decoder_functions }, { NULL, } };