Skip to content

Fix spurious error queue entries while loading private keys#15283

Closed
DDvO wants to merge 2 commits intoopenssl:masterfrom
siemens:fix_spurious_key_load_errors
Closed

Fix spurious error queue entries while loading private keys#15283
DDvO wants to merge 2 commits intoopenssl:masterfrom
siemens:fix_spurious_key_load_errors

Conversation

@DDvO
Copy link
Contributor

@DDvO DDvO commented May 14, 2021

This removes spurious error queue entries such as

ossl_store_handle_load_result:crypto/store/store_result.c:151:error: unsupported:
asn1_check_tlen:crypto/asn1/tasn_dec.c:1156:error: wrong tag:
asn1_item_embed_d2i:crypto/asn1/tasn_dec.c:322:error: nested asn1 error:Type=EC_PRIVATEKEY

when loading private keys via OSSL_STORE: .
The underlying issue was revealed by a recent improvement in #15253.

On this occasion, also

  • Make OSSL_CRMF_ENCRYPTEDVALUE_get1_encCert() conservative on the error queue
  • crypto/{http,cmp}: Remove obsolete comments w.r.t. ERR_clear_error()

@DDvO DDvO added approval: otc review pending triaged: bug The issue/pr is/fixes a bug labels May 14, 2021
@DDvO DDvO force-pushed the fix_spurious_key_load_errors branch from 77811c0 to b97067a Compare May 14, 2021 13:58
@DDvO DDvO closed this May 15, 2021
@DDvO DDvO reopened this May 15, 2021
@DDvO DDvO force-pushed the fix_spurious_key_load_errors branch from b97067a to 5f4caa7 Compare May 15, 2021 05:28
@levitte
Copy link
Member

levitte commented May 15, 2021

Do you have an example that resulted in that spurious error queue entry?

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

Do you have an example that resulted in that spurious error queue entry?

Sure.

wget 'http://pki.certificate.fi:8081/install-ca-cert.html/ca-certificate.crt?ca-id=632&download-certificate=1' -O insta.ca.crt
apps/openssl ecparam -genkey -name prime256v1 -out insta.priv.pem
apps/openssl cmp -config apps/openssl.cnf -section insta

yields

cmp_main:apps/cmp.c:2581:CMP info: using section(s) 'insta' of OpenSSL configuration file 'apps/openssl.cnf'
setup_client_ctx:apps/cmp.c:1891:CMP info: will contact http://pki.certificate.fi:8700/pkix/
ossl_store_handle_load_result:crypto/store/store_result.c:151:CMP error: unsupported:
asn1_check_tlen:crypto/asn1/tasn_dec.c:1156:CMP error: wrong tag:
asn1_item_embed_d2i:crypto/asn1/tasn_dec.c:322:CMP error: nested asn1 error:Type=EC_PRIVATEKEY
send_receive_check:crypto/cmp/cmp_client.c:167:CMP info: sending IR
send_receive_check:crypto/cmp/cmp_client.c:187:CMP info: received IP
send_receive_check:crypto/cmp/cmp_client.c:167:CMP info: sending CERTCONF
send_receive_check:crypto/cmp/cmp_client.c:187:CMP info: received PKICONF
save_free_certs:apps/cmp.c:1940:CMP info: received 1 extra certificate(s), saving to file 'insta.extracerts.pem'
save_free_certs:apps/cmp.c:1940:CMP info: received 1 enrolled certificate(s), saving to file 'insta.cert.pem'

When loading RSA keys instead this does not happen.

Update: A simpler way to reproduce this is:

apps/openssl ecparam -genkey -name prime256v1 -out privkey-with-params.pem
apps/openssl pkey -in  privkey-with-params.pem

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

With the proposed changes there is a test failure on

make run_tests TESTS=test_x509

which appears to indicate a different type of uncleanness regarding the error queue:

25-test_x509.t .. 1/18 
../../util/wrap.pl ../../apps/openssl x509 -in ../../test/certs/v3-certs-RC2.p12 -passin 'pass:v3-certs' 2> out.txt => 1
Could not read certificate from ../../test/certs/v3-certs-RC2.p12
Unable to load certificate
not ok 17 - load v3-certs-RC2 no asn1 errors
25-test_x509.t .. 17/18 --------------------------------------------------------
25-test_x509.t .. Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/18 subtests 

@levitte
Copy link
Member

levitte commented May 15, 2021

Ah, the spurious error happens because insta.priv.pem holds two items, demonstrated like this:

$ ./util/wrap.pl apps/openssl storeutl insta.priv.pem 
0: Parameters
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
1: Pkey
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgB3KN9BGnVLlGtTRR
QJTqyn2fS1OH390SzH5NRSLpZB+hRANCAAT95NrJeMI0BP50A/Yx0G88ZY1EJiP7
wOi12pAaMM+nN5iyed17ro8Y4JgIEJcSAt0rNo24FPHb013p/PrEeLXb
-----END PRIVATE KEY-----
Total found: 2

The point where this file is loaded is here:

EVP_PKEY *pkey = load_key(uri, format, 0, pass_string, eng, desc);

That will, of course, only accept private keys, not key params.

This has me think about recent development, where we're taking a closer look at the extected STORE_INFO type in ossl_store_handle_load_result() and its sub-functions. It seems to me that the semantics of what this function returns has shifted slightly because we're not making a difference between an error when decoding the contents and that the data doesn't match the expected STORE_INFO type. ossl_store_handle_load_result() should return true, with a NULL *v for the latter, and since it currently doesn't, the NULL STORE_INFO is returned all the way here, and you get that spurious error printed:

openssl/apps/lib/apps.c

Lines 978 to 991 in 773f1c3

/*
* This can happen (for example) if we attempt to load a file with
* multiple different types of things in it - but the thing we just
* tried to load wasn't one of the ones we wanted, e.g. if we're trying
* to load a certificate but the file has both the private key and the
* certificate in it. We just retry until eof.
*/
if (info == NULL) {
if (OSSL_STORE_error(ctx)) {
ERR_print_errors(bio_err);
ERR_clear_error();
}
continue;
}

So, it seems to come down to store_result.c and what it does when the data doesn't match expectations.

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

Indeed the errors occur only with the

 -----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----

portion of the private key file!
And their

ossl_store_handle_load_result:crypto/store/store_result.c:151:CMP error: unsupported:

part is unclear (if given at all, it should mention that it does not like the parameters found), while

asn1_check_tlen:crypto/asn1/tasn_dec.c:1156:CMP error: wrong tag:
asn1_item_embed_d2i:crypto/asn1/tasn_dec.c:322:CMP error: nested asn1 error:Type=EC_PRIVATEKEY

is misleading because the problem is not the EC PRIVATE KEY part but the EC PARAMETERS part.

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

Actually also

ossl_store_handle_load_result:crypto/store/store_result.c:151:CMP error: unsupported:

is misleading in my view since EC PARAMETERS are not unsupported, just unexpected.

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

So I suspect that in

    ERR_set_mark();
    if (*v == NULL && !try_name(&helper_data, v))
        goto err;
    ERR_pop_to_mark();
    ERR_set_mark();
    if (*v == NULL && !try_key(&helper_data, v, ctx, provider, libctx, propq))
        goto err;
    ERR_pop_to_mark();
    ERR_set_mark();
    if (*v == NULL && !try_cert(&helper_data, v, libctx, propq))
        goto err;
    ERR_pop_to_mark();
    ERR_set_mark();
    if (*v == NULL && !try_crl(&helper_data, v, libctx, propq))
        goto err;
    ERR_pop_to_mark();
    ERR_set_mark();
    if (*v == NULL && !try_pkcs12(&helper_data, v, ctx, libctx, propq))
        goto err;
    ERR_pop_to_mark();

a handling for the (EC) PARAMETERS case (OSSL_STORE_INFO_PARAMS) is missing?

@DDvO DDvO force-pushed the fix_spurious_key_load_errors branch from c41b255 to 8d5ceb6 Compare May 15, 2021 11:01
@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

I've just found a very simple preliminary fix for the reported spurious update: 'unsupported' error problem,
but not for the deeper ones we discovered here during the analysis.

@levitte, will you take care of a deeper/proper solution (which is not urgent in my view)?

@levitte
Copy link
Member

levitte commented May 15, 2021

Please drop your changes to providers/implementations/encode_decode/decode_der2key.c

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

Please drop your changes to providers/implementations/encode_decode/decode_der2key.c

Unfortunately these are still needed to prevent

asn1_check_tlen:crypto/asn1/tasn_dec.c:1156:CMP error: wrong tag:
asn1_item_embed_d2i:crypto/asn1/tasn_dec.c:322:CMP error: nested asn1 error:Type=EC_PRIVATEKEY

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

Or what else do you concretely suggest to get rid of those?

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

And is my latest quick-fix change to ossl_store_handle_load_result():

    if (*v == NULL && helper_data.object_type == OSSL_OBJECT_PKEY)
        return 0;

acceptable (for the time being) after all?

@levitte
Copy link
Member

levitte commented May 15, 2021

Or what else do you concretely suggest to get rid of those?

I would have a ERR_set_mark() / ERR_pop_to_mark() pair in here:

openssl/apps/lib/apps.c

Lines 978 to 991 in 773f1c3

/*
* This can happen (for example) if we attempt to load a file with
* multiple different types of things in it - but the thing we just
* tried to load wasn't one of the ones we wanted, e.g. if we're trying
* to load a certificate but the file has both the private key and the
* certificate in it. We just retry until eof.
*/
if (info == NULL) {
if (OSSL_STORE_error(ctx)) {
ERR_print_errors(bio_err);
ERR_clear_error();
}
continue;
}

and simply remove the error printing

@levitte
Copy link
Member

levitte commented May 15, 2021

@levitte, will you take care of a deeper/proper solution (which is not urgent in my view)?

Yes, I plan on doing so.

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

@levitte, will you take care of a deeper/proper solution (which is not urgent in my view)?

Yes, I plan on doing so.

Pleased to hear.

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

Or what else do you concretely suggest to get rid of those?

I would have a ERR_set_mark() / ERR_pop_to_mark() pair in here:

openssl/apps/lib/apps.c

Lines 978 to 991 in 773f1c3

/*
* This can happen (for example) if we attempt to load a file with
* multiple different types of things in it - but the thing we just
* tried to load wasn't one of the ones we wanted, e.g. if we're trying
* to load a certificate but the file has both the private key and the
* certificate in it. We just retry until eof.
*/
if (info == NULL) {
if (OSSL_STORE_error(ctx)) {
ERR_print_errors(bio_err);
ERR_clear_error();
}
continue;
}

and simply remove the error printing

This appears strange to me. For instance like this?

            ERR_set_mark();
            if (OSSL_STORE_error(ctx)) {
#if 0
                ERR_print_errors(bio_err);
                ERR_clear_error();
#endif
            }
            ERR_pop_to_mark();

As expected, this (or having the pair outside the block) does not prevent the error printed by

apps/openssl cmp -config apps/openssl.cnf -section insta

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

As explained above, I am pretty sure that the trial-and-error construct in der2key_decode() needs to be adapted.

@DDvO
Copy link
Contributor Author

DDvO commented May 15, 2021

How if we simply defer this (as WIP) to the beta phase?
Or if we commit my preliminary solution but mark it as, e.g. "TODO(3.0) improve" and open an issue for that?

@levitte
Copy link
Member

levitte commented May 15, 2021

I would like to ask you for a little patience. I intend to use the weekend for something else.

@openssl-machine
Copy link
Collaborator

This PR is in a state where it requires action by @openssl/otc but the last update was 30 days ago

@openssl-machine
Copy link
Collaborator

This PR is in a state where it requires action by @openssl/otc but the last update was 61 days ago

@mattcaswell
Copy link
Member

Is this needed for 3.0? It does not have the 3.0 milestone at the moment

@DDvO DDvO requested review from levitte and t8m December 20, 2021 10:28
@DDvO
Copy link
Contributor Author

DDvO commented Dec 20, 2021

I lost track of this PR on September,
but after yesterday's reminder by the openssl-machine,
I've just brushed it up, making it ready for further (hopefully final) reviewing.

@t8m t8m added approval: otc review pending and removed waiting-for: contributor response This pull request is awaiting a response by the contributor labels Dec 20, 2021
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 not a constant time operation.

Copy link
Contributor Author

@DDvO DDvO Dec 20, 2021

Choose a reason for hiding this comment

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

I do not see why this could be problem here.
Using ERR_clear_error() was too aggressive since it deletes all errors, also unrelated ones.
And using ERR_set_mark(); ... ERR_pop_to_mark(); is the usual pattern to get rid of any - and only those - error queue entries potentially added by a function call.
If there is a more efficient way of achieving this please let me know.

Copy link
Member

Choose a reason for hiding this comment

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

Please note that the following code uses constant time operation. And ERR_clear_error() is constant time unless allocated err data is present.

Copy link
Contributor Author

@DDvO DDvO Dec 20, 2021

Choose a reason for hiding this comment

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

Ah, now I see what you are after.

Hmm, looks like even ERR_clear_error() is not really constant-time due to OPENSSL_free(es->err_file[i]), where es->err_file[i] should be NULL if and only if no entry is present at index i.

And I doubt that constant-time behavior is relevant here because at this point an encrypted newly enrolled certificate (which BTW is extremely rare) has been received and is being decrypted. This only happens under the control of the client requesting a certficate and does not happen often.

Copy link
Contributor Author

@DDvO DDvO Dec 20, 2021

Choose a reason for hiding this comment

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

The constant_time_* calls go back to #9774 (comment) and #9774 (comment).
To me, following the "best practice" mentioned there appears not really need.

If you prefer, I could move the ERR_pop_to_mark(); after those.

Copy link
Member

Choose a reason for hiding this comment

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

Please remove the set_mark/pop_to_mark as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Adding the set_mark/pop_to_mark was the actual point of that commit.
As explained, using ERR_clear_error() was too harsh.
We should remove only those entries that were (potentially) added by EVP_PKEY_decrypt().

Copy link
Member

Choose a reason for hiding this comment

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

I mean there is no point in clearing anything as there should be nothing security sensitive in error queue under the same assumptions that lead to removing the consttime calls.

Copy link
Contributor Author

@DDvO DDvO Dec 21, 2021

Choose a reason for hiding this comment

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

Ah, right, so we can simply get rid also of the ERR_clear_error() - even better!
Done so, rebasing and squashing the changes to crmf_lib.c.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to the ongoing exchange on #17319,
I've moved the changes to crmf_lib.c form this PR to an independent one: #17354.

@DDvO DDvO requested a review from t8m December 21, 2021 16:09
@t8m t8m added the triaged: refactor The issue/pr requests/implements refactoring label Dec 21, 2021
@DDvO DDvO force-pushed the fix_spurious_key_load_errors branch from 4416068 to 942e668 Compare December 21, 2021 17:38
@DDvO DDvO force-pushed the fix_spurious_key_load_errors branch from 942e668 to 1196fe5 Compare December 25, 2021 12:43
@DDvO DDvO added the branch: 3.0 Applies to openssl-3.0 branch label Jan 4, 2022
@DDvO
Copy link
Contributor Author

DDvO commented Jan 4, 2022

@t8m, can we please finalize this now,
since the changes still under discussion have been moved to #17354?

@DDvO DDvO requested a review from t8m January 4, 2022 17:24
Copy link
Member

@t8m t8m left a comment

Choose a reason for hiding this comment

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

It would be nice to have some testcases to test for error reporting. But that is for different PR I think.

@t8m t8m added approval: done This pull request has the required number of approvals and removed approval: otc review pending triaged: refactor The issue/pr requests/implements refactoring labels Jan 4, 2022
@openssl-machine openssl-machine added approval: ready to merge The 24 hour grace period has passed, ready to merge and removed approval: done This pull request has the required number of approvals labels Jan 5, 2022
@openssl-machine
Copy link
Collaborator

This pull request is ready to merge

openssl-machine pushed a commit that referenced this pull request Jan 6, 2022
openssl-machine pushed a commit that referenced this pull request Jan 6, 2022
Reviewed-by: Tomas Mraz <[email protected]>
(Merged from #15283)

(cherry picked from commit da198ad)
@DDvO
Copy link
Contributor Author

DDvO commented Jan 6, 2022

Merged - thanks @levitte and @t8m

@DDvO DDvO closed this Jan 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approval: ready to merge The 24 hour grace period has passed, ready to merge branch: master Applies to master branch branch: 3.0 Applies to openssl-3.0 branch triaged: bug The issue/pr is/fixes a bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants