Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix API to allow zero-length secret for EVP_KDF #12826

Closed
wants to merge 4 commits into from

Conversation

jon-oracle
Copy link
Contributor

The behaviour of EVP_KDF_CTX_set_params changed between alpha4 and current, so that one of our tests started to fail. The test results in this API call:

params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, (char *) mdname, 0);
params[1] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SECRET, secret, secretlen);
params[2] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SEED, seed, seedlen);
params[3] = OSSL_PARAM_construct_end();

ret = EVP_KDF_CTX_set_params(ctx, params);

Where:
secret = pointer to blank buffer
secretlen = 0

The KDF is OSSL_KDF_NAME_TLS1_PRF.

This started to fail recently with a change to the HMAC code (which is used internally in the KDF):

3fddbb2

The code added to allocate a copy of the HMAC key is not zero-length aware so the OPENSSL_secure_malloc fails. The KDF code to store the secret is already zero-length aware.

This PR also goes some way to fixing #8531 although the secret buffer is still checked for NULL so the user would have to provide a non-null buffer even with a length of zero.

@kaduk
Copy link
Contributor

kaduk commented Sep 8, 2020

It's like #11920 all over again...

@kaduk kaduk added approval: otc review pending branch: master Merge to master branch triaged: bug The issue/pr is/fixes a bug labels Sep 8, 2020
@kaduk kaduk added this to the 3.0.0 milestone Sep 8, 2020
@paulidale
Copy link
Contributor

I think that there might be a better fix for this.

There is an issue where OPENSSL_secure_malloc(0) returns a pointer to memory if it's been set up and NULL if not. OPENSSL_malloc(0) always returns NULL. POSIX specifies that malloc(0) can either return NULL or a unique pointer that can be later freed.

I suspect changing OPENSSL_malloc(0) to return a pointer to a single byte is a better fix because it addresses the underlying difference between out APIs.

@paulidale paulidale added approval: done This pull request has the required number of approvals hold: need otc decision The OTC needs to make a decision and removed approval: otc review pending labels Sep 8, 2020
@paulidale
Copy link
Contributor

paulidale commented Sep 8, 2020

Scratch that, fixing malloc causes test failures...

in kdf code

@paulidale paulidale removed the hold: need otc decision The OTC needs to make a decision label Sep 8, 2020
@slontis
Copy link
Member

slontis commented Sep 9, 2020

Is this behavior what we really want - or should it have caused an error?

@jon-oracle
Copy link
Contributor Author

Is this behavior what we really want - or should it have caused an error?

I'm not going to make a call either way. If it should have caused an error then this should have been generated when the API was called, not left to a malloc failure later. Also if you prefer to fail on a zero-length secret then maybe you should also fail on any insecure secret length? I doubt a one-byte secret is providing much authentication.

@slontis
Copy link
Member

slontis commented Sep 9, 2020

I'm not going to make a call either way. If it should have caused

Are you just doing negative testing or is this a use case you actually need?

@jon-oracle
Copy link
Contributor Author

I'm not going to make a call either way. If it should have caused

Are you just doing negative testing or is this a use case you actually need?

This is an edge-case test. I can't see a specific use case.

@kaduk
Copy link
Contributor

kaduk commented Sep 9, 2020

I think we do see empty-keyed HMAC pop up in some protocol edge cases, though most of the examples that are coming to mind are using a block of zeros rather than a true empty key. The original report to me that prompted #11920 was definitely for HMAC; something QUIC-related IIRC.

@paulidale
Copy link
Contributor

Would a zero length secret be permitted by NIST for a module integrity check?

I'm sure there are plenty of examples of this in the wild 😞

@openssl-machine
Copy link
Collaborator

24 hours has passed since 'approval: done' was set, but as this PR has been updated in that time the label 'approval: ready to merge' is not being automatically set. Please review the updates and set the label manually.

Copy link
Member

@slontis slontis left a comment

Choose a reason for hiding this comment

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

If we are going to allow this then there at least needs to be a test for it.

@jon-oracle
Copy link
Contributor Author

Some tests added. It seems that the initial bug reported in #12409 is no longer a problem.

test/evp_kdf_test.c Outdated Show resolved Hide resolved
Copy link
Member

@slontis slontis left a comment

Choose a reason for hiding this comment

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

Thanks jon

test/evp_kdf_test.c Outdated Show resolved Hide resolved
@t8m t8m requested a review from kaduk September 15, 2020 17:04
@paulidale paulidale added approval: review pending This pull request needs review by a committer and removed approval: done This pull request has the required number of approvals labels Sep 15, 2020
Copy link
Contributor

@paulidale paulidale left a comment

Choose a reason for hiding this comment

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

Great 😁

@kroeckx kroeckx modified the milestones: 3.0.0, 3.0.0 beta1 Sep 16, 2020
@@ -205,6 +205,12 @@ static int kbkdf_derive(void *vctx, unsigned char *key, size_t keylen)
return 0;
}

/* Fail if the output length is zero */
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting that KBKDF gets this comment but not HKDF or tls1_prf.

static int test_kdf_tls1_prf(void)
{
int ret;
EVP_KDF_CTX *kctx = NULL;
unsigned char out[16];
OSSL_PARAM params[4], *p = params;
OSSL_PARAM *params;
Copy link
Contributor

Choose a reason for hiding this comment

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

Basically every other test in this file uses stack-allocated OSSL_PARAM arrays; I didn't see discussion of why this one should allocate on the heap. (It's not wrong per se, just surprising.)
You can keep the helper function and just pass in a pointer to the start of the (stack-allocated) array, if you want.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm ambivalent either way. A comment might be beneficial to explain the change of style. However, there are plenty of cases where two different styles are used (param construct vs param_bld e.g.). One minor point in favour of the status quo is that construct_tls1_prf_params is returning a filled out param array, so doing it dynamically like this provides some future proofing against needing more params at some point (it's pretty weak).

EVP_KDF_CTX_free(kctx);
OPENSSL_free(params);
return ret;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It's vaguely plausible that some of these tls-prf tests could be refactored consolidated into an ADD_ALL_TESTS (vs ADD_TEST) invocation, but I didn't do the work to check and won't insist that anyone else do the work.

*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
salt, strlen(salt));
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
(unsigned char*)key, keylen);
Copy link
Contributor

Choose a reason for hiding this comment

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

If we have to make any other changes I'd suggest reordering these to match the order of the function parameters. It's purely cosmetic, though, so not critical.

EVP_KDF_CTX_free(kctx);
OPENSSL_free(params);
return ret;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

[same coment about consolidating/refactoring]

|| !TEST_true(EVP_KDF_CTX_set_params(kctx, params))
/* A key length that is too small should fail */
|| !TEST_int_eq(EVP_KDF_derive(kctx, out, 112 / 8 - 1), 0)
|| !TEST_int_eq(EVP_KDF_derive(kctx, out, 112 / 8 - 1), 0))
Copy link
Contributor

Choose a reason for hiding this comment

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

I assume there's not an appropriate #define for 112 here.

OSSL_PARAM *params;

if (sizeof(len) > 32)
len = SIZE_MAX;
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems incorrect/bizzare (but is unchanged from the old code). len is a size_t, which is not really of variable size (and 256-bit integers are not really a thing, let alone bigger ones!).


/* If the "pkcs5" mode is disabled then the derive will now fail */
if (!TEST_true(EVP_KDF_CTX_set_params(kctx, mode_params))
|| !TEST_int_eq(EVP_KDF_derive(kctx, out, sizeof(out)), 0))
Copy link
Contributor

Choose a reason for hiding this comment

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

This actually raises a potentially interesting question: should EVP_KDF_derive() be a one-shot operation on a given EVP_KDF_CTX?

@kaduk
Copy link
Contributor

kaduk commented Sep 16, 2020

(That's a +0 from me; I read the whole thing and there are no critical flaws, and I'm okay with it proceeding as-is with the other reviews if that's desired.)

@paulidale
Copy link
Contributor

@kaduk, does this mean another review is required or does your earlier stand as enough of a positive for this to go in?

@kaduk
Copy link
Contributor

kaduk commented Sep 17, 2020

@paulidale oops, I seem to have misread the preexisting approval count. Mine would have been enough for it to go in, I think, but Tim has short-circuited the issue in the time it took me to check my email.

@paulidale
Copy link
Contributor

@kaduk, it was your final +0 comment that threw me. I didn't want to assume that your approval stood as a +1.
All good now.

@paulidale paulidale added approval: done This pull request has the required number of approvals and removed approval: review pending This pull request needs review by a committer labels Sep 17, 2020
openssl-machine pushed a commit that referenced this pull request Sep 17, 2020
Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #12826)
openssl-machine pushed a commit that referenced this pull request Sep 17, 2020
Also add more test cases

Reviewed-by: Tim Hudson <tjh@openssl.org>
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from #12826)
@paulidale
Copy link
Contributor

Merged to master after removing some end of line spaces. Thanks.

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 Merge to master branch triaged: bug The issue/pr is/fixes a bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants