Skip to content

Param locate helper functions.#8519

Closed
paulidale wants to merge 5 commits intoopenssl:masterfrom
paulidale:param-locate
Closed

Param locate helper functions.#8519
paulidale wants to merge 5 commits intoopenssl:masterfrom
paulidale:param-locate

Conversation

@paulidale
Copy link
Contributor

Add a number of functions that combine a get/set with a locate.

  • documentation is added or updated
  • tests are added or updated

@levitte
Copy link
Member

levitte commented Mar 19, 2019

As long as the diverse _get and _set functions in this set think a NULL param is an error, I'm opposed to further "simplification" that will solidify a coding pattern that's already bad. See the change in #8523, that should hopefully explain matters a bit.

@paulidale
Copy link
Contributor Author

Agree that we must avoid having bad patterns in the code (or adding any more...)

I had an idea: these new functions return 1 on success, 0 on failure and -1 for not found. This allows mandatory params via: if (OSSL_PARAM_locate_set_XXX(...) <= 0) error; and non-mandatory via: if (OSSL_PARAM_locate_set_XXX(...)) error;. It isn't the cleanest but it would be workable.

I think we're going to see a lot of both types, so making a short coding pattern for each seems useful.

The other possibilities I can think of are an extra argument or another set of calls to distinguish between the two. Of these, I prefer the latter.

@paulidale
Copy link
Contributor Author

I think it is only the set functions that need to care about about mandatory or not. The get functions will always have to check if the value was retrieved or not.

@levitte
Copy link
Member

levitte commented Mar 19, 2019

I think it is only the set functions that need to care about about mandatory or not. The get functions will always have to check if the value was retrieved or not.

What set functions and what get functions? If it's this API you're talking about, i.e. the likes of OSSL_PARAM_set_utf8_string, it should be the other way around.

@levitte
Copy link
Member

levitte commented Mar 19, 2019

Also, I think that it's not up to this API to decide what's mandatory and what's not. For example, for an RSA key that can have numbers assigned (which would be typical for our default provider, yeah?), it would be perfectly plausible to only set the 'n' and 'e' numbers, without making it mandatory to specify the rest of the possible numbers.

@paulidale
Copy link
Contributor Author

What set functions and what get functions? If it's this API you're talking about, i.e. the likes of OSSL_PARAM_set_utf8_string, it should be the other way around.

The example that triggered this in #8513 is setting things that were not in the params array. The setters are required. Does your comment mean the getters aren't? I can't think of a use case where param is got but the caller doesn't need to check if it existed.

I've added a number of OSSL_PARAM_optional_set_XXX() calls to distinguish the two usages.

@levitte
Copy link
Member

levitte commented Mar 19, 2019

What set functions and what get functions? If it's this API you're talking about, i.e. the likes of OSSL_PARAM_set_utf8_string, it should be the other way around.

The example that triggered this in #8513 is setting things that were not in the params array. The setters are required. Does your comment mean the getters aren't? I can't think of a use case where param is got but the caller doesn't need to check if it existed.

And here I see where I think you get confused. The function in question is deflt_get_params, and the typical would be that any get_params functions use the _set_ from the OSSL_PARAM API and vice versa (simple logic; one ends wants to get the params of an object means that the other ends get to set the chunks of memory pointed at by the passed OSSL_PARAM array).

I've added a number of OSSL_PARAM_optional_set_XXX() calls to distinguish the two usages.

You will need a set of OSSL_PARAM_optional_get_XXX() functions as well.

@paulidale
Copy link
Contributor Author

I see the source of the confusion. Thanks for the clarification.

@levitte
Copy link
Member

levitte commented Mar 19, 2019

I insist, there should be optional_get functions as well

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I disagree with the error, but good catch on the NULL check. The code should be:

    if ((p = OSSL_PARAM_locate(params, "foo")) != NULL)
        OSSL_PARAM_set_utf8_ptr(p, "foo value");
    if ((p = OSSL_PARAM_locate(params, "bar")) != NULL)
        OSSL_PARAM_set_utf8_ptr(p, "bar value");
    if ((p = OSSL_PARAM_locate(params, "cookie")) != NULL)
        OSSL_PARAM_set_utf8_ptr(p, "cookie value");

The rationale is still the same, if the other end is only interested in the cookie value, why should it be obligated to also get the foo and bar values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, the RSA example would work better. n and e being mandatory, the rest being optional?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the caller is only interested in getting the values of p and q (if those are accessible)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

    OSSL_PARAM_optional_set_utf8_ptr(params, "foo", "foo value");
    OSSL_PARAM_optional_set_utf8_string(params, "bar", "bar value");
    OSSL_PARAM_optional_set_utf8_string(params, "cookie", "cookie value");

Copy link
Member

@levitte levitte Mar 19, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you really want to make an valuable example of mandatory parameters, it would be to use a mixture of locate_get and optional_get functions. Personally, I rather imagine that some parameters might have to be grouped together. For example, that if "foo" is set by the caller, so must "bar".

@paulidale
Copy link
Contributor Author

I've added the optional get functions, although I still don't see why.
I've reverted the example.

return OSSL_PARAM_set_octet_ptr(OSSL_PARAM_locate(p, key), val, used_len);
}

int OSSL_PARAM_optional_get_int(const OSSL_PARAM *p, const char *key, int *val)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The optional "get" forms don't seem very useful. With a "success" return you can't tell whether key and val have been updated or not. Applications would be better off just using OSSL_PARAM_locate_get_int which returns fail if it wasn't found.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say we have a provider that offers RSA, and with which applications (or libcrypto) can set the RSA object parameters n, e and d.

The application (or libcrypto for that matter) wants to create a public key, so tries this:

    OSSL_PARAM params[3];
    size_t n = 0;
    unsigned char e_buf[16];
    unsigned char n_buf[512];

    BN_bn2nativepad(rsa_e, e_buf, sizeof(e_buf));
    BN_bn2nativepad(rsa_n, n_buf, sizeof(n_buf));

    params[n++] = OSSL_PARAM_construct_BN("e", rsa_e, sizeof(rsa_e), NULL);
    params[n++] = OSSL_PARAM_construct_BN("n", rsa_n, sizeof(rsa_n), NULL);
    params[n].key = NULL;

    if (!EVP_PKEY_set_params(rsa_pkey, params))
        fprintf(stderr, "ERROR\n");

Now, on the provider end, set_params is defined like this:

static int rsa_set_params(void *vrsa, const OSSL_PARAM *params)
{
    PROV_RSA *rsa = vrsa;
    return OSSL_PARAM_locate_get_BN(params, "n", rsa->n)
        && OSSL_PARAM_locate_get_BN(params, "e", rsa->e)
        && OSSL_PARAM_locate_get_BN(params, "d", rsa->d);
}

With this construction, creating a public key with parameters fails.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before saying that this won't happen, consider test/testrsapub.pem. How do you imagine that this file will become a default provider PKEY?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, so "d" truly is optional in this case. So write it like this:

static int rsa_set_params(void *vrsa, const OSSL_PARAM *params)
{
    PROV_RSA *rsa = vrsa;

    OSSL_PARAM_locate_get_BN(params, "d", rsa->d);
    return OSSL_PARAM_locate_get_BN(params, "n", rsa->n)
        && OSSL_PARAM_locate_get_BN(params, "e", rsa->e);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And then, another dude just wants to sign with a private key and passes d and n...

And oh, what if the passed d param was invalid?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it could be left as an exercise to those who write the receiving end to figure out what values have to be grouped together and how to code that properly?

I'm getting worried that this API gets over engineered and tries to solve everything instead of being a set of tools. That comes with a cost.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to lose the optional get calls in favour of the receiver figuring it out.

Copy link
Member

@levitte levitte Mar 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way I see it now, that speaks for this PR as a whole.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll close unless somebody does think this is beneficial.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to agree.

@richsalz
Copy link
Contributor

Adding several dozen functions to avoid one line of code seems like the wrong trade-off to me.
OpenSSL already gets dinged for the size of its API, and adding all these things that will have to live forever seems like a really bad mistake.

@paulidale
Copy link
Contributor Author

It's one line of code that will be repeated a large number of times.

@richsalz
Copy link
Contributor

it's one line of code that will be repeated a large number of times.

So, let's write the code without these dozens of functions and then refactor out the common case.

It will be a lot more clear to the programmer what is going on, rather than recalling the semantics of dozens of new functions.


params[n++] = OSSL_PARAM_construct_BN("n", n_buf, sizeof(n_buf), NULL);
params[n++] = OSSL_PARAM_construct_uint32("e", rsa_e, NULL);
params[n] = OSSL_PARAM_END;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, have you tested that this line goes through a compiler?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered if anyone would notice :)

@paulidale paulidale closed this Mar 20, 2019
@paulidale paulidale deleted the param-locate branch September 19, 2019 02:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants