From 246a1f3dfafc4a377bc7d7da65d9f8981a696abd Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Wed, 3 Jul 2019 18:42:21 +0200 Subject: [PATCH] Add OSSL_PARAM_construct_from_text() and OSSL_PARAM_allocate_from_text() These are utility functions that can be used to replace calls to ctrl_str type functions with get_params / set_params types of calls. They work by translating text values to something more suitable for OSSL_PARAM, and by interpretting parameter keys in a compatible fashion. Reviewed-by: Paul Dale (Merged from https://github.com/openssl/openssl/pull/9303) --- crypto/build.info | 2 +- crypto/params_from_text.c | 219 ++++++++++++++++++++ doc/man3/OSSL_PARAM_construct_from_text.pod | 167 +++++++++++++++ include/openssl/params.h | 10 + util/libcrypto.num | 2 + 5 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 crypto/params_from_text.c create mode 100644 doc/man3/OSSL_PARAM_construct_from_text.pod diff --git a/crypto/build.info b/crypto/build.info index 0b203a7f93..2130382034 100644 --- a/crypto/build.info +++ b/crypto/build.info @@ -67,7 +67,7 @@ SOURCE[../providers/fips]=$CORE_COMMON # Central utilities $UTIL_COMMON=\ - cryptlib.c params.c bsearch.c ex_data.c o_str.c \ + cryptlib.c params.c params_from_text.c bsearch.c ex_data.c o_str.c \ ctype.c threads_pthread.c threads_win.c threads_none.c initthread.c \ context.c sparse_array.c asn1_dsa.c packet.c param_build.c $CPUIDASM $UTIL_DEFINE=$CPUIDDEF diff --git a/crypto/params_from_text.c b/crypto/params_from_text.c new file mode 100644 index 0000000000..2a861c94fe --- /dev/null +++ b/crypto/params_from_text.c @@ -0,0 +1,219 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. 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 +#include +#include + +/* + * When processing text to params, we're trying to be smart with numbers. + * Instead of handling each specific separate integer type, we use a bignum + * and ensure that it isn't larger than the expected size, and we then make + * sure it is the expected size... if there is one given. + * (if the size can be arbitrary, then we give whatever we have) + */ + +static int prepare_from_text(const OSSL_PARAM *paramdefs, const char *key, + const char *value, size_t value_n, + /* Output parameters */ + const OSSL_PARAM **paramdef, int *ishex, + size_t *buf_n, BIGNUM **tmpbn) +{ + const OSSL_PARAM *p; + + /* + * ishex is used to translate legacy style string controls in hex format + * to octet string parameters. + */ + *ishex = strncmp(key, "hex", 3) == 0; + + if (*ishex) + key += 3; + + p = *paramdef = OSSL_PARAM_locate_const(paramdefs, key); + if (p == NULL) + return 0; + + switch (p->data_type) { + case OSSL_PARAM_INTEGER: + case OSSL_PARAM_UNSIGNED_INTEGER: + if (*ishex) + BN_hex2bn(tmpbn, value); + else + BN_dec2bn(tmpbn, value); + + if (*tmpbn == NULL) + return 0; + + /* + * 2s complement negate, part 1 + * + * BN_bn2nativepad puts the absolute value of the number in the + * buffer, i.e. if it's negative, we need to deal with it. We do + * it by subtracting 1 here and inverting the bytes in + * construct_from_text() below. + */ + if (p->data_type == OSSL_PARAM_INTEGER && BN_is_negative(*tmpbn) + && !BN_sub_word(*tmpbn, 1)) { + return 0; + } + + *buf_n = BN_num_bytes(*tmpbn); + + /* + * TODO(v3.0) is this the right way to do this? This code expects + * a zero data size to simply mean "arbitrary size". + */ + if (p->data_size > 0) { + if (*buf_n >= p->data_size) { + CRYPTOerr(0, CRYPTO_R_TOO_SMALL_BUFFER); + /* Since this is a different error, we don't break */ + return 0; + } + /* Change actual size to become the desired size. */ + *buf_n = p->data_size; + } + break; + case OSSL_PARAM_UTF8_STRING: + if (*ishex) { + CRYPTOerr(0, ERR_R_PASSED_INVALID_ARGUMENT); + return 0; + } + *buf_n = strlen(value) + 1; + break; + case OSSL_PARAM_OCTET_STRING: + if (*ishex) { + *buf_n = strlen(value) >> 1; + } + break; + } + + return 1; +} + +static int construct_from_text(OSSL_PARAM *to, const OSSL_PARAM *paramdef, + const char *value, size_t value_n, int ishex, + void *buf, size_t buf_n, BIGNUM *tmpbn) +{ + if (buf == NULL) + return 0; + + switch (paramdef->data_type) { + case OSSL_PARAM_INTEGER: + case OSSL_PARAM_UNSIGNED_INTEGER: + /* + { + if ((new_value = OPENSSL_malloc(new_value_n)) == NULL) { + BN_free(a); + break; + } + */ + + BN_bn2nativepad(tmpbn, buf, buf_n); + + /* + * 2s complement negate, part two. + * + * Because we did the first part on the BIGNUM itself, we can just + * invert all the bytes here and be done with it. + */ + if (paramdef->data_type == OSSL_PARAM_INTEGER + && BN_is_negative(tmpbn)) { + unsigned char *cp; + size_t i = buf_n; + + for (cp = buf; i-- > 0; cp++) + *cp ^= 0xFF; + } + break; + case OSSL_PARAM_UTF8_STRING: + strncpy(buf, value, buf_n); + break; + case OSSL_PARAM_OCTET_STRING: + if (ishex) { + size_t l = 0; + + if (!OPENSSL_hexstr2buf_ex(buf, buf_n, &l, value)) + return 0; + } else { + memcpy(buf, value, buf_n); + + } + break; + } + + *to = *paramdef; + to->data = buf; + to->data_size = buf_n; + to->return_size = 0; + + return 1; +} + +int OSSL_PARAM_construct_from_text(OSSL_PARAM *to, + const OSSL_PARAM *paramdefs, + const char *key, const char *value, + size_t value_n, + void *buf, size_t *buf_n) +{ + const OSSL_PARAM *paramdef = NULL; + int ishex = 0; + BIGNUM *tmpbn = NULL; + int ok = 0; + + if (to == NULL || paramdefs == NULL) + return 0; + + if (!prepare_from_text(paramdefs, key, value, value_n, + ¶mdef, &ishex, buf_n, &tmpbn)) + return 0; + + /* + * The user gets the expected buffer size back even if the buffer isn't + * allocated. + */ + if (buf == NULL) + return 1; + + ok = construct_from_text(to, paramdef, value, value_n, ishex, + buf, *buf_n, tmpbn); + BN_free(tmpbn); + return ok; +} + +int OSSL_PARAM_allocate_from_text(OSSL_PARAM *to, + const OSSL_PARAM *paramdefs, + const char *key, const char *value, + size_t value_n) +{ + const OSSL_PARAM *paramdef = NULL; + int ishex = 0; + void *buf = NULL; + size_t buf_n = 0; + BIGNUM *tmpbn = NULL; + int ok = 0; + + if (to == NULL || paramdefs == NULL) + return 0; + + if (!prepare_from_text(paramdefs, key, value, value_n, + ¶mdef, &ishex, &buf_n, &tmpbn)) + return 0; + + if ((buf = OPENSSL_malloc(buf_n)) == NULL) { + CRYPTOerr(0, ERR_R_MALLOC_FAILURE); + return 0; + } + + ok = construct_from_text(to, paramdef, value, value_n, ishex, + buf, buf_n, tmpbn); + BN_free(tmpbn); + return ok; +} diff --git a/doc/man3/OSSL_PARAM_construct_from_text.pod b/doc/man3/OSSL_PARAM_construct_from_text.pod new file mode 100644 index 0000000000..28d5e6fc13 --- /dev/null +++ b/doc/man3/OSSL_PARAM_construct_from_text.pod @@ -0,0 +1,167 @@ +=pod + +=head1 NAME + +OSSL_PARAM_construct_from_text, OSSL_PARAM_allocate_from_text +- OSSL_PARAM construction utilities + +=head1 SYNOPSIS + + #include + + int OSSL_PARAM_construct_from_text(OSSL_PARAM *to, + const OSSL_PARAM *paramdefs, + const char *key, const char *value, + size_t value_n, + void *buf, size_t *buf_n) + int OSSL_PARAM_allocate_from_text(OSSL_PARAM *to, + const OSSL_PARAM *paramdefs, + const char *key, const char *value, + size_t value_n); + +=head1 DESCRIPTION + +With OpenSSL before version 3.0, parameters were passed down to or +retrieved from algorithm implementations via control functions. +Some of these control functions existed in variants that took string +parameters, for example L. + +OpenSSL 3.0 introduces a new mechanism to do the same thing with an +array of parameters that contain name, value, value type and value +size (see L for more information). + +OSSL_PARAM_construct_from_text() takes a control I, I and +value size I, and given a parameter descriptor array +I, it converts the value to something suitable for +L and stores that in the buffer I, and modifies +the parameter I to match. +I, if not NULL, will be assigned the number of bytes used in +I. +If I is NULL, only I will be modified, everything else is +left untouched, allowing a caller to find out how large the buffer +should be. +I needs to be correctly aligned for the type of the B +I. + +OSSL_PARAM_allocate_from_text() works like OSSL_PARAM_construct_from_text(), +except it allocates the buffer internally. +The caller must remember to free the data of I when it's not +useful any more. + +For parameters having the type B, +B, or B, both +functions will interpret the I differently if the key starts +with "hex". +In that case, the value is decoded first, and the result will be used +as parameter value. + +=head1 RETURN VALUES + +OSSL_PARAM_construct_from_text() and OSSL_PARAM_allocate_from_text() +returns 1 on success, and 0 on error. + +=head1 NOTES + +The parameter descriptor array comes from functions dedicated to +return them. +The following B attributes are used: + +=over 4 + +=item I + +=item I + +=item I + +=back + +All other attributes are ignored. + +The I attribute can be zero, meaning that the parameter it +describes expects arbitrary length data. + +=head1 EXAMPLE + +Code that looked like this: + + int mac_ctrl_string(EVP_PKEY_CTX *ctx, const char *value) + { + int rv; + char *stmp, *vtmp = NULL; + stmp = OPENSSL_strdup(value); + if (!stmp) + return -1; + vtmp = strchr(stmp, ':'); + if (vtmp) { + *vtmp = 0; + vtmp++; + } + rv = EVP_MAC_ctrl_str(ctx, stmp, vtmp); + OPENSSL_free(stmp); + return rv; + } + + ... + + + char *macopt; + for (i = 0; i < sk_OPENSSL_STRING_num(macopts); i++) { + macopt = sk_OPENSSL_STRING_value(macopts, i); + if (pkey_ctrl_string(mac_ctx, macopt) <= 0) { + BIO_printf(bio_err, + "MAC parameter error \"%s\"\n", macopt); + ERR_print_errors(bio_err); + goto mac_end; + } + } + +Can be written like this instead: + + OSSL_PARAM *params = + OPENSSL_zalloc(sizeof(OSSL_PARAM) + * (sk_OPENSSL_STRING_num(opts) + 1)); + const OSSL_PARAM *paramdefs = EVP_MAC_CTX_set_param_types(mac); + size_t params_n; + + for (params_n = 0; params_n < (size_t)sk_OPENSSL_STRING_num(opts); + params_n++) { + char *opt = sk_OPENSSL_STRING_value(opts, (int)params_n); + char *stmp, *vtmp = NULL; + + if ((stmp = OPENSSL_strdup(opt)) == NULL + || (vtmp = strchr(stmp, ':')) == NULL + || (*vtmp++ = '\0') /* Always zero */ + || !OSSL_PARAM_allocate_from_text(¶ms[params_n], + paramdefs, + stmp, vtmp, strlen(vtmp))) { + BIO_printf(bio_err, "MAC parameter error '%s'\n", opt); + ERR_print_errors(bio_err); + goto err; + } + } + params[params_n] = OSSL_PARAM_construct_end(); + if (!EVP_MAC_CTX_set_params(ctx, params)) { + BIO_printf(bio_err, "MAC parameter error\n"); + ERR_print_errors(bio_err); + goto err; + } + for (; params_n-- > 0;) { + OPENSSL_free(params[params_n].data); + } + OPENSSL_free(params); + +=head1 SEE ALSO + +L, L + +=head1 COPYRIGHT + +Copyright 2019 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 +L. + +=cut diff --git a/include/openssl/params.h b/include/openssl/params.h index 0e830dee0d..8293ee2cf0 100644 --- a/include/openssl/params.h +++ b/include/openssl/params.h @@ -89,6 +89,16 @@ OSSL_PARAM OSSL_PARAM_construct_octet_ptr(const char *key, void **buf, size_t bsize); OSSL_PARAM OSSL_PARAM_construct_end(void); +int OSSL_PARAM_construct_from_text(OSSL_PARAM *to, + const OSSL_PARAM *paramdefs, + const char *key, const char *value, + size_t value_n, + void *buf, size_t *buf_n); +int OSSL_PARAM_allocate_from_text(OSSL_PARAM *to, + const OSSL_PARAM *paramdefs, + const char *key, const char *value, + size_t value_n); + int OSSL_PARAM_get_int(const OSSL_PARAM *p, int *val); int OSSL_PARAM_get_uint(const OSSL_PARAM *p, unsigned int *val); int OSSL_PARAM_get_long(const OSSL_PARAM *p, long int *val); diff --git a/util/libcrypto.num b/util/libcrypto.num index c6f1f2d7ad..ac861fec6b 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4708,3 +4708,5 @@ EC_KEY_new_ex 4817 3_0_0 EXIST::FUNCTION:EC EC_KEY_new_by_curve_name_ex 4818 3_0_0 EXIST::FUNCTION:EC OPENSSL_hexstr2buf_ex 4819 3_0_0 EXIST::FUNCTION: OPENSSL_buf2hexstr_ex 4820 3_0_0 EXIST::FUNCTION: +OSSL_PARAM_construct_from_text 4821 3_0_0 EXIST::FUNCTION: +OSSL_PARAM_allocate_from_text 4822 3_0_0 EXIST::FUNCTION: -- 2.39.5