-
-
Notifications
You must be signed in to change notification settings - Fork 11k
hkdf: allow salt to be reset to null #27305
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
Conversation
| if (!TEST_int_gt(EVP_PKEY_derive_init(pctx), 0) | ||
| || !TEST_int_gt(EVP_PKEY_CTX_set_hkdf_md(pctx, EVP_sha256()), 0) | ||
| || !TEST_int_gt(EVP_PKEY_CTX_set1_hkdf_salt(pctx, fake, | ||
| sizeof(fake) - 1), 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not really overjoyed with how this does sizeof(salt) - 1 and sizeof(info)-1..
These are just 0 values.
Where is the test for setting salt to NULL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The line below sets a zero length salt (this was done by the existing test). This is needed because the default is a zero length salt and even though the set after this fails, it doesn't return an error so this test worked by fluke rather than design. Adding this set breaks the default and the next line has to work for the test to pass.
| ctx->salt = NULL; | ||
| if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, | ||
| &ctx->salt_len)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be more correct allowing for using NULL, 0 for the param?
| ctx->salt = NULL; | |
| if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, | |
| &ctx->salt_len)) | |
| ctx->salt = NULL; | |
| ctx->salt_len = 0; | |
| if (p->data_size != 0 && p->data != NULL | |
| && !OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, | |
| &ctx->salt_len)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The length and NULL check were introduced by the earlier fix and they are broken in a subtle way. Subtle enough that both you and I missed the problem when reviewing the change 😢
I thought about leaving these checks in but the big issue for me is that we don't do it this way anywhere else. Consistency in our APIs is something we should be striving for.
We do not generally:
- check for
->data_size != 0 - check for
->data == NULL
If the param exists we just assume it has a valid pointer associated with it and that the length is trustworthy. It seemed reasonable to do the same here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @t8m is correct. Such a check is necessary because OSSL_PARAM_get_octet_string() fails if the param's data is NULL. This currently actually prevents setting the salt to NULL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @t8m is correct. Such a check is necessary because
OSSL_PARAM_get_octet_string()fails if the param'sdatais NULL. This currently actually prevents setting the salt to NULL.
Not sure if this was an intended change, but we found this statement to be true. We can no longer set the salt to NULL in openssl 3.5.1. We get the following error:
80476A18AA750000:error:078C0102:common libcrypto routines:get_string_internal:passed a null parameter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could never set the salt to NULL before 3.5.1. If you tried the salt remained unchanged and the call apparently suceeded. So if you ever set the salt to a non–empty value, you could not set it back to empty†. What changed in 3.5.1 is that trying to set the salt to NULL raises an error. I think this is better than silently doing nothing and it's consistent with other parameter usage.
I see a few ways forward:
- if, as is likely, you never set the salt explicitly, there is no need to reset it;
- if you do set the salt explicitly, you're in a world of hurt because the previous salt will have been used without you knowing or
- set the salt to empty by passing
""and a length of zero instead of a NULL pointer.
In 3.4.0, line 310 means your attempt to set the parameter is silently ignored:
openssl/providers/implementations/kdfs/hkdf.c
Lines 309 to 317 in 636dfad
| if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) { | |
| if (p->data_size != 0 && p->data != NULL) { | |
| OPENSSL_free(ctx->salt); | |
| ctx->salt = NULL; | |
| if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, | |
| &ctx->salt_len)) | |
| return 0; | |
| } | |
| } |
In 3.5.1 this changed to:
openssl/providers/implementations/kdfs/hkdf.c
Lines 309 to 315 in f4fb4a6
| if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) { | |
| OPENSSL_free(ctx->salt); | |
| ctx->salt = NULL; | |
| if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, | |
| &ctx->salt_len)) | |
| return 0; | |
| } |
The call on line 313 raises an error if the parameter's data pointer is NULL.
†: the reset call sets the salt to empty but also wipes everything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you are having to set it and then reset it to NULL then something is also wrong. The RFC for HKDF says its optional, but it also says you should normally set it if possible because its more secure. The case where you dont set it should be if you just cant due to platform related restrictions. So you should probably always be setting it (if you can), or not setting it at all. In either of these cases setting it to NULL is not required.
|
Ping for reviews? |
|
This pull request is ready to merge |
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from #27305)
|
Pushed to master, 3.5, 3.4, 3.3, 3.2 and 3.0. |
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Fixes openssl#27302 Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Fixes openssl#27302 Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305) (cherry picked from commit 12eb6c5)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305) (cherry picked from commit 12eb6c5)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Fixes openssl#27302 Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Fixes openssl#27302 Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
Fixes openssl#27302 Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from openssl#27305)
This contains an API change that disallows setting the salt to a null value. See: openssl/openssl#27305 This seems to be the opposite of the intended effect of that change, but this patch includes a workaround nonetheless. Co-Authored-By: devgianlu <altomanigianluca@gmail.com>
|
The downstream impact of this seems to have not been particularly appropriate for a security patch release. For example, I cannot upgrade to 3.5.1 now without also updating strongswan to pull in fixes to adapt to the behavior change: https://github.com/strongswan/strongswan/commits/master/src/libstrongswan/plugins/openssl/openssl_kdf.c. I see that other consumers are referenced in recent notes here. I get the argument that what the consumers were counting on was wrong behavior anyway, but the point is that we generally do not expect for security patch releases to force code changes elsewhere... |
|
We didn't classify this as a security problem, it's just a bug. |
|
I understand that this wasn't a security issue and that's actually exactly why I'm a bit tweaked that the behavior change made its way into patch releases. Let's suppose the quarterly patch actually addressed something more critical than CVE-2025-4575. I have to update to the latest patch in whatever minor release I'm on (happens to be 3.5) to plug a security hole, but then that breaks something else (strongswan in my case). Now I'm stuck with either updating that too (presuming they changed their code) or living with the security issue. It's not exactly ideal. Like if I'm going 3.4 to 3.5, this is the kind of thing I expect to bump into... but from 3.5.0 to 3.5.1 it was a rather unpleasant surprise. It seems to me that having cherry-picked this back into patch release demonstrates either a lack of understanding how widely the existing (albeit incorrect) behavior was actually being depended on by some consumers or else not viewing the use case of trying to update OpenSSL from patch-to-patch within a minor release without downstream impacts as being something that matters much to most of the community. |
The fix in #24141 wasn't quite correct. The test case just happened to work on a freshly initialised context.