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

sha2: fix incorrect sha256 hash issue #7

Closed
wants to merge 4 commits into from

Conversation

cyphar
Copy link
Contributor

@cyphar cyphar commented Jun 7, 2023

As mentioned in #4, with newer GCC versions, our sha256 digest methods would
return incorrect hashes. This is due to undefined behaviour in sha2.c when
compiling with -fstrict-aliasing (which is implied by -O2, the default
compilation mode under autoconf and by most distributions).

The solution is to force autoconf to set -fno-strict-aliasing. In addition,
add a Github Actions workflow to run make check to make sure we can catch
this is if it ever happens in the future.

Fixes #4
Signed-off-by: Aleksa Sarai cyphar@cyphar.com

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Our sha2 code has undefined behaviour when compiled with
-fstrict-aliasing (applied by default with -O2 in GCC), and in GCC >=
11, this undefined behaviour results in incorrect sha256 digests to be
returned from libnbcompat.

Autoconf is not really meant to be used to set specific compiler flags
(since they are non-portable by definition) but the most portable way of
adding these is to use the macros provided by autoconf-archive. The new
m4/ scripts are from version v2023.02.20 of autoconf-archive[1].

[1]: https://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=commit;h=v2023.02.20

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This ensures that we link with whatever form of libnbcompat autoconf
feels is correct. Note that autoconf makes sure that we are using the
locally-compiled .so as well (in this configuration, the *-test
"binaries" are actually shell scripts that set LD_LIBRARY_PATH among
many other hacks).

I'm not sure whether this change makes any real difference in practice,
but it is probably the way that you're "supposed" to link against a
libtool-ised library within the same project.

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
This should ensure that we never miss another hashing issue (at least
among the hash functions we currently test).

Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
@cyphar
Copy link
Contributor Author

cyphar commented Jun 7, 2023

You can verify that the new Github Action would've caught the issue by taking a look at this run in my test repo, where the -fno-strict-aliasing fix wasn't applied.

@cyphar
Copy link
Contributor Author

cyphar commented Jun 7, 2023

An alternative patch is to fix the aliasing issue (the actual problem is only in SHA256_Final and SHA512_Last -- it's because of the fact that context->buffer is being used in a textbook example of what -fstrict-aliasing considers undefined behaviour):

diff --git a/sha2.c b/sha2.c
index bdcbd317bdd4..4c9436f98275 100644
--- a/sha2.c
+++ b/sha2.c
@@ -567,7 +567,7 @@ void SHA256_Final(sha2_byte digest[SHA256_DIGEST_LENGTH], SHA256_CTX* context) {
                        *context->buffer = 0x80;
                }
                /* Set the bit count: */
-               *(sha2_word64*)(void *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount;
+               memcpy(&context->buffer[SHA256_SHORT_BLOCK_LENGTH], &context->bitcount, sizeof(context->bitcount));

                /* Final transform: */
                SHA256_Transform(context, (sha2_word32*)(void *)context->buffer);
@@ -870,8 +870,8 @@ static void SHA512_Last(SHA512_CTX* context) {
                *context->buffer = 0x80;
        }
        /* Store the length of input data (in bits): */
-       *(sha2_word64*)(void *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1];
-       *(sha2_word64*)(void *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0];
+       memcpy(&context->buffer[SHA512_SHORT_BLOCK_LENGTH], &context->bitcount[1], sizeof(*context->bitcount));
+       memcpy(&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8], &context->bitcount[0], sizeof(*context->bitcount));

        /* Final transform: */
        SHA512_Transform(context, (sha2_word64*)(void *)context->buffer);

Let me know which you prefer.

@cyphar cyphar marked this pull request as draft June 7, 2023 13:10
@archiecobbs
Copy link
Owner

Hah, sorry - just saw you already figured out the same patch as me in 864c1cf. Thanks.

@archiecobbs archiecobbs closed this Jun 7, 2023
@cyphar
Copy link
Contributor Author

cyphar commented Jun 7, 2023

This also adds CI in the form of Github Actions to check the tests won't fail -- is that something you'd be interested in?

archiecobbs added a commit that referenced this pull request Jun 7, 2023
Contributed by Aleksa Sarai.
@archiecobbs
Copy link
Owner

Oh, didn't see that - sorry.

Yes looks good, added in d2f836b.

Thanks!

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.

sha256 digests do not match test vectors
2 participants