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 <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/9303)
This commit is contained in:
Richard Levitte 2019-07-03 18:42:21 +02:00
parent 82bd7c2cbd
commit 246a1f3dfa
5 changed files with 399 additions and 1 deletions

View file

@ -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

219
crypto/params_from_text.c Normal file
View file

@ -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 <string.h>
#include <openssl/err.h>
#include <openssl/params.h>
/*
* 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,
&paramdef, &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,
&paramdef, &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;
}

View file

@ -0,0 +1,167 @@
=pod
=head1 NAME
OSSL_PARAM_construct_from_text, OSSL_PARAM_allocate_from_text
- OSSL_PARAM construction utilities
=head1 SYNOPSIS
#include <openssl/params.h>
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<EVP_PKEY_CTX_ctrl_str(3)>.
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<OSSL_PARAM(3)> for more information).
OSSL_PARAM_construct_from_text() takes a control I<key>, I<value> and
value size I<value_n>, and given a parameter descriptor array
I<paramdefs>, it converts the value to something suitable for
L<OSSL_PARAM(3)> and stores that in the buffer I<buf>, and modifies
the parameter I<to> to match.
I<buf_n>, if not NULL, will be assigned the number of bytes used in
I<buf>.
If I<buf> is NULL, only I<buf_n> will be modified, everything else is
left untouched, allowing a caller to find out how large the buffer
should be.
I<buf> needs to be correctly aligned for the type of the B<OSSL_PARAM>
I<key>.
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<to> when it's not
useful any more.
For parameters having the type B<OSSL_PARAM_INTEGER>,
B<OSSL_PARAM_UNSIGNED_INTEGER>, or B<OSSL_PARAM_OCTET_STRING>, both
functions will interpret the I<value> 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<OSSL_PARAM> attributes are used:
=over 4
=item I<key>
=item I<data>
=item I<data_size>
=back
All other attributes are ignored.
The I<data_size> 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(&params[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<OSSL_PARAM(3)>, L<OSSL_PARAM_TYPE(3)>
=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<https://www.openssl.org/source/license.html>.
=cut

View file

@ -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);

View file

@ -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: