Skip to content

Improve the usability of the ca app using EdDSA#6901

Closed
mattcaswell wants to merge 3 commits intoopenssl:masterfrom
mattcaswell:improve-ca-eddsa
Closed

Improve the usability of the ca app using EdDSA#6901
mattcaswell wants to merge 3 commits intoopenssl:masterfrom
mattcaswell:improve-ca-eddsa

Conversation

@mattcaswell
Copy link
Member

Previously you had to supply "null" as the digest to use EdDSA. This changes
things so that any digest is ignored.

Previously you had to supply "null" as the digest to use EdDSA. This changes
things so that any digest is ignored.
* EVP_PKEY_get_default_digest_nid() returns 2 if the digest is
* mandatory for this algorithm.
*/
if (def_ret == 2 && def_nid == NID_undef) {
Copy link
Contributor

Choose a reason for hiding this comment

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

dislike magic numbers, but okay if this is out of scope for this PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

So do I, but I thought it was moving the scope too far to tackle that in this PR.

Copy link
Member

Choose a reason for hiding this comment

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

On an additional note: we say that if it returns 2 the digest is mandatory for this algorithm, but we act in that direction only if the return is 2 and the required digest is NID_undef.

What if the required default was something else, e.g. SM3 for SM2 or an engine-provided PKEY method with such requirements? Shouldn't it be honored?

Copy link
Member Author

Choose a reason for hiding this comment

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

@romen, perhaps it should - but again I think that starts moving the scope of this PR too far away from the original problem.

Copy link
Member

@romen romen Aug 22, 2018

Choose a reason for hiding this comment

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

@mattcaswell I understand that.

My issue is that I'm just not convinced that, in this case, usability can trample on backwards behavioral compatibility.

Is it really necessary that Ed25519/448 should ignore explicit configuration lines/cmdline args?
I ask because to introduce this we are adding a very specific (only for (EVP_PKEY_get_default_digest_nid() == 2) && (returned_nid == NID_undef)) behavior in the apps, which does not match exactly the documented behavior of the function (returns 2 if the digest is mandatory for this algorithm).

Copy link
Member Author

Choose a reason for hiding this comment

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

Is that a break in backwards compat? There are lots of things that the apps can now do in 1.1.1 that they couldn't do before. Try pointing a 1.1.0 s_client at a TLSv1.3 only server....it will fail. Do the same in 1.1.1 and it will work!

Copy link
Member Author

@mattcaswell mattcaswell Aug 22, 2018

Choose a reason for hiding this comment

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

More concerning for backwards compat would be if there are things in 1.1.0 that used to work, but now don't in 1.1.1.

Choose a reason for hiding this comment

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

@romen Because if you run the same commands with 1.1.0 apps (with access to Ed25519 through an engine) and 1.1.1 apps, using the same configuration file for the pki settings, the former fails while the second succeeds.

The whole point of the PR is to succeed where we previously failed. The failure is spurious, the public key algorithm does not use a digest for signing, so we should succeed regardless of any specified digest. Requiring the user to explicitly specify a null digest is a bad UI and breaks existing configuration files and command-line scripts.

If a particular public key algorithm works exclusively with a particular digest or no digest, then we should always use that (or no) digest, and ignore options that provide a digest to use for those key types that could use one of many.

@mattcaswell I think it is time to go ahead and push this PR, I've not seen any substantive objections that prevent moving forward. If someone wants to fine-tune with a follow-on PR we can evaluate that separately.

Copy link
Member

Choose a reason for hiding this comment

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

If a particular public key algorithm works exclusively with a particular digest or no digest, then we should always use that (or no) digest, and ignore options that provide a digest to use for those key types that could use one of many.

@vdukhovni but we don't do this with this changes, only for the "no digest", that's why I am talking about "special case" and not conforming to the documented behavior: if the function returns 2 but it's not asking for the null digest then we still fail!

@mattcaswell I don't mean to stop the PR, I raised my objections and discussion points, which were evaluated, so I am satisfied anyway!

Choose a reason for hiding this comment

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

I am aware that this PR does not address the case of a single non-null mandatory digest. We can address that issue when we come to it. This PR is strictly an improvement, that addresses the immediate issue. We can generalize later, but feel free to propose a follow-on PR if you have something ready to go.

@richsalz richsalz added approval: done This pull request has the required number of approvals branch: master Applies to master branch labels Aug 9, 2018
@romen
Copy link
Member

romen commented Aug 9, 2018

I guess this is perfectly fine for now.

I only want to raise the issue of thinking about a future when instead of only PureEdDSA OpenSSL might support also HashEdDSA: would this change have an unintended impact on configuration patterns or command line arguments that might be difficult to break in future versions?

I honestly don't know the answer, nor if such issues would concern the guarantees in minor or major releases.

@mattcaswell
Copy link
Member Author

I only want to raise the issue of thinking about a future when instead of only PureEdDSA OpenSSL might support also HashEdDSA: would this change have an unintended impact on configuration patterns or command line arguments that might be difficult to break in future versions?

I did consider this, but I don't think it is an issue. The way it has been implemented the ca app asks the EVP_PKEY object what its default hash is. Only if the default hash is undefined is it allowed to not have a hash specified. If we later implement hash EdDSA then the EVP_PKEY will return with a sensible, defined, hash.

@romen
Copy link
Member

romen commented Aug 9, 2018

Thanks for clarifying it @mattcaswell!

@beldmit
Copy link
Member

beldmit commented Aug 9, 2018

It seems to me that req.c be fixed the similar way to ca.c.

@petrovr
Copy link

petrovr commented Aug 9, 2018

I'm not sure that code should ignore configuration settings. Correction for #6201 , i.e. null digest is fine with me.

@mattcaswell
Copy link
Member Author

I'm not sure that code should ignore configuration settings. Correction for #6201 , i.e. null digest is fine with me

The current solution is causing some significant usability problems, so it doesn't seem to be viable:

https://mta.openssl.org/pipermail/openssl-users/2018-August/008473.html

This follows on from the previous commit, and makes the same change to
ignore the digest if we are using EdDSA.
@mattcaswell
Copy link
Member Author

I added a new commit to make an equivalent change in the req app. Interestingly, while updating the documentation for that I discovered that we already ignore the digest configuration setting in req in some situations so this approach seems consistent with that.

@mattcaswell
Copy link
Member Author

@richsalz please reconfirm

@richsalz
Copy link
Contributor

richsalz commented Aug 9, 2018

reconfirm. good catch, @petrovr !

@mattcaswell
Copy link
Member Author

I think the catch was @beldmit...!

@petrovr
Copy link

petrovr commented Aug 9, 2018 via email

@mattcaswell
Copy link
Member Author

My point is that program code can not ignore invalid values:

  1. null is valid value,
  2. missing digest could mean use default , i.e. null
  3. other values, for instance sha1, should break processing

I disagree. It is perfectly reasonable to want to set a default digest of (say) sha256 for most stuff, but not use it when doing EdDSA.

@petrovr
Copy link

petrovr commented Aug 9, 2018 via email

@mattcaswell
Copy link
Member Author

I have the required approval for this, and I still think this is the correct approach, but given @petrovr's objection I invite any other comment from @openssl/committers.

@t-j-h
Copy link
Member

t-j-h commented Aug 10, 2018

I don't see the parameter as forcing a digest value conceptually.

I think defining default_digest to mean the digest for algorithms that allow a settable digest is a reasonable approach (and the documentation should be updated to make that clear that this is the definition of the configuration parameter - not that some algorithms ignore it - that it only applies to algorithms that allow settable values). That way it is clearer what is going on here and looks less like a workaround.

Basically more clarity in the docs wording - but I agree with @mattcaswell in terms of the approach.

Copy link

@vdukhovni vdukhovni left a comment

Choose a reason for hiding this comment

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

One could take the view that each "ca" instance is associated with a particular key, and should use a dedicated openssl.cnf for that instance, with an appropriate default_md. But, sadly, this is not what users do, and having the software fail by default is simply not a good UI. So I'm in favour of this approach. An explicit -md _mumble_ will still fail when _mumble_ is not compatible with the key. That's good enough.


ecx_free,
ecx_ctrl,
ecx_xctrl,
Copy link

Choose a reason for hiding this comment

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

Lets keep the name ecx_ctrl


ecx_free,
0,
ecx_edctrl,
Copy link

Choose a reason for hiding this comment

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

Please use ecd_crtl for consistency with other ed... specific functions

@petrovr
Copy link

petrovr commented Aug 11, 2018

It is about how to change existing code :
...
if (md == NULL && (md = lookup_conf(conf, section, ENV_DEFAULT_MD)) == NULL)
goto end;
...
, i.e. parameter "default_md" has to be specified either on command line or in configuration section.
Above could be split as follow:
...
if (md == null) md = lookup_conf(...)
if (md == null) {
if md is required : goto end
else md = "default"
}
...
I expect above code to allow user to use default_md=default and with proposed modifications (correction in existing function and addition of new one) for ASN1_PKEY_CTRL_DEFAULT_MD_NID "null" digest to be used.

Also think about "default" to be set unconditionally in above code snippet.

@mattcaswell
Copy link
Member Author

I expect above code to allow user to use default_md=default and

That would require a user wishing to use EdDSA, along with other algorithms to only use the "default" setting. Setting it to (say) sha256 would rule out the use of EdDSA - which I think is incorrect.

@mattcaswell
Copy link
Member Author

Fix up commit pushed renaming the ecx_ctrl functions as suggested.

An explicit -md mumble will still fail when mumble is not compatible with the key. That's good enough.

Just to be clear an explicit -md _mumble_ will succeed with EdDSA after this PR, but will fail with other algorithms where _mumble_ is not compatible. I think that is what you were saying anyway, but just wanted to be sure.

@vdukhovni please can you reconfirm after the ctrl function name change commit?

@mattcaswell mattcaswell added this to the 1.1.1 milestone Aug 13, 2018
Copy link

@vdukhovni vdukhovni left a comment

Choose a reason for hiding this comment

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

Yep, the new names look better.

@petrovr
Copy link

petrovr commented Aug 15, 2018

I don't like to stop progress on this issue, i.e. approvals.

For protocol I did tests with following code snipped from apps/ca.c

if (md == NULL)
    md = lookup_conf(conf, section, ENV_DEFAULT_MD);

if (md == NULL)
    md = "default";

if (strcmp(md, "null") == 0) {
    dgst = EVP_md_null();
} else {
    if (strcmp(md, "default") == 0) {
        int def_nid;
        if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) <= 0) {
            BIO_puts(bio_err, "no default digest\n");
            goto end;
        }
        /* NID_undef if signing algorithm requires there to be no digest */
        dgst = (def_nid == NID_undef)
            ? EVP_md_null()
            : EVP_get_digestbynid(def_nid);
    }
}
if (dgst == NULL) {
    if (!opt_md(md, &dgst))
        goto end;
}

This was my idea. Let me know if someone found above generic approach useful. Is so I open an enhancement request.
In all cases go ahead with current status.

Copy link
Member

@romen romen left a comment

Choose a reason for hiding this comment

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

I would also note that this change will introduce a substantial behavior change from previous versions: e.g. if an engine provided ed25519 to version 1.1.0, using the same command for the ca app with the same cmdline arguments and the same configuration file will not yield the same results.

Specifically on 1.1.0 one must either provide -md null or -md default as an argument or alter the configuration file.

Breaking the assumption that the ca app will act based on the arguments/configuration might be something that should be done in a major release rather than in a minor one.

I understand that this is done for usability, but at the least I think that an entry in CHANGES and NEWS about changing the behavior of the ca app is required, as in this case it is disruptive of compatibility with 1.1.0 for 3rd party engines.

* EVP_PKEY_get_default_digest_nid() returns 2 if the digest is
* mandatory for this algorithm.
*/
if (def_ret == 2 && def_nid == NID_undef) {
Copy link
Member

Choose a reason for hiding this comment

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

On an additional note: we say that if it returns 2 the digest is mandatory for this algorithm, but we act in that direction only if the return is 2 and the required digest is NID_undef.

What if the required default was something else, e.g. SM3 for SM2 or an engine-provided PKEY method with such requirements? Shouldn't it be honored?

* for this algorithm.
*/
if (EVP_PKEY_get_default_digest_nid(pkey, &def_nid) == 2
&& def_nid == NID_undef) {
Copy link
Member

Choose a reason for hiding this comment

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

Also on req we honor the return value 2 only for NID_undef

Copy link
Member Author

Choose a reason for hiding this comment

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

As above I think this is a valid point, but I don't want to tackle that in this PR.

This option specifies the digest algorithm to use. Any digest supported by the
OpenSSL B<dgst> command can be used. This option can be overridden on the
command line. Certain signing algorithms (i.e. Ed25519 and Ed448) will ignore
any digest that has been set.
Copy link
Member

Choose a reason for hiding this comment

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

This is a behavior change, should it warrant an HISTORY entry?

Copy link
Member Author

Choose a reason for hiding this comment

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

Is it really a behaviour change? There aren't any algorithms in 1.1.0 where this clause applies. You gave the example somewhere else of someone providing an engine to do ed25519 in 1.1.0. I'm not convinced that is actually possible. In 1.1.0 we assume that all signing algorithms require a digest...and stuff will probably break if you try to supply one via an engine where that isn't true.

Copy link
Member

Choose a reason for hiding this comment

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

I commented on the behavior change explicitly because I noticed that it started breaking stuff for an engine I am working on (libsuola) which does exactly this, and I noticed that after this change 1.1.1 apps have the option of ignoring explicitly set configuration entries or cmdline args, while 1.1.0 apps don't.

I just wanted to make sure that all the implications of this change are discussed before committing to them! ;)

Copy link
Member Author

Choose a reason for hiding this comment

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

I noticed that it started breaking stuff for an engine I am working on

Interesting. Can you be more explicit about in what way it broke?

message digest NID for the public key signature operations associated with key
B<pkey>.
B<pkey>. Note that some signature algorithms (i.e. Ed25519 and Ed448) do not use
a digest during signing. In this case B<pnid> will be set to NID_undef.
Copy link
Member

@romen romen Aug 16, 2018

Choose a reason for hiding this comment

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

If I am not mistaken, up to 1.1.0 returning NID_undef as the default md would cause errors in the OpenSSL apps.

If that's the case, and this change is ok for a minor release, an HISTORY entry detailing the change might be required.

Copy link
Member

Choose a reason for hiding this comment

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

On further investigation, I am mistaken on this, so nevermind this comment.

@beldmit
Copy link
Member

beldmit commented Aug 16, 2018

BTW, why not to specify a separate NID, not NID_undef for md_null? It can save us from separate processing NID_undef as dgst_nid here and may be in some other places of code.

@kaduk
Copy link
Contributor

kaduk commented Aug 17, 2018

BTW, why not to specify a separate NID, not NID_undef for md_null? It can save us from separate processing NID_undef as dgst_nid here and may be in some other places of code.

One could perhaps argue that the "Intrinsic" TLS HashAlgorithm defined by RFC 8422 (section 5.1.3) would argue for having such a dedicated new NID, but if we do add such a thing, we'd need to assign it some semantics for all the other places it could be used, and it's unclear that we really want to do that.

levitte pushed a commit that referenced this pull request Aug 22, 2018
Previously you had to supply "null" as the digest to use EdDSA. This changes
things so that any digest is ignored.

Reviewed-by: Viktor Dukhovni <[email protected]>
(Merged from #6901)
levitte pushed a commit that referenced this pull request Aug 22, 2018
This follows on from the previous commit, and makes the same change to
ignore the digest if we are using EdDSA.

Reviewed-by: Viktor Dukhovni <[email protected]>
(Merged from #6901)
@mattcaswell
Copy link
Member Author

For reasons I don't entirely understand (or agree with), this PR seems to have been more controversial than I expected. However I still believe this is the correct approach, and it has all the relevant approvals and support from an OMC member, with no "-1" from any committer, so I have pushed. If people want to they can follow up with further PRs as appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: done This pull request has the required number of approvals branch: master Applies to master branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants