Rework truncated SHA-2 to silence GCC 12 false positive warning.
GCC 12's -Wstringop-overflow flags issues in SHA224_Final, etc., because
it calls into generic code that might output a SHA-224 length or a
SHA-256 length, and the function prototype declares the array is only
sized for SHA-224.
This is a bit messy because OpenSSL's API for the truncated SHA-2 hashes
allows you to mix and match them. The output size is set by SHA224_Init
and then, originally, SHA256_Final and SHA224_Final were the same thing.
See how OpenSSL's own SHA224 function calls SHA224_Init + SHA256_Final:
https://github.com/openssl/openssl/blob/OpenSSL_1_1_1q/crypto/sha/sha256.c#L49-L61
To get the function prototype bounds to work out, we tightened this
slightly in
https://boringssl-review.googlesource.com/c/boringssl/+/47807 and added
an assert to SHA224_Final that ctx->md_len was the right size.
SHA256_Final does not have that assert yet. The assert says that mixing
SHA256_Init and SHA224_Final is a caller error.
This isn't good enough for GCC 12, which checks bounds assuming there is
no external invariant on ctx->md_len. This CL changes the behavior of
the shorter Final functions: they will now always output the length
implied by the function name. ctx->md_len only figures into an assert()
call. As we don't have the assert in the untruncated functions yet, I've
preserved their behavior, but the test run with cl/471617180 should tell
us whether apply this to all functions is feasible.
Update-Note: Truncated SHA-2 Final functions change behavior slightly,
but anyone affected by this behavior change would already have tripped
an assert() in debug builds.
Change-Id: I80fdcbe6ad76bc8713c0f2de329b958a2b35e8ae
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/54246
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/sha/sha256.c b/crypto/fipsmodule/sha/sha256.c
index 454b947..046f6e2 100644
--- a/crypto/fipsmodule/sha/sha256.c
+++ b/crypto/fipsmodule/sha/sha256.c
@@ -133,7 +133,7 @@
return SHA256_Update(ctx, data, len);
}
-static int sha256_final_impl(uint8_t *out, SHA256_CTX *c) {
+static int sha256_final_impl(uint8_t *out, size_t md_len, SHA256_CTX *c) {
crypto_md32_final(&sha256_block_data_order, c->h, c->data, SHA256_CBLOCK,
&c->num, c->Nh, c->Nl, /*is_big_endian=*/1);
@@ -141,12 +141,12 @@
// 'final' function can fail. SHA-512 does not have a corresponding check.
// These functions already misbehave if the caller arbitrarily mutates |c|, so
// can we assume one of |SHA256_Init| or |SHA224_Init| was used?
- if (c->md_len > SHA256_DIGEST_LENGTH) {
+ if (md_len > SHA256_DIGEST_LENGTH) {
return 0;
}
- assert(c->md_len % 4 == 0);
- const size_t out_words = c->md_len / 4;
+ assert(md_len % 4 == 0);
+ const size_t out_words = md_len / 4;
for (size_t i = 0; i < out_words; i++) {
CRYPTO_store_u32_be(out, c->h[i]);
out += 4;
@@ -162,14 +162,14 @@
// |SHA256_Final| and expects |sha->md_len| to carry the size over.
//
// TODO(davidben): Add an assert and fix code to match them up.
- return sha256_final_impl(out, c);
+ return sha256_final_impl(out, c->md_len, c);
}
int SHA224_Final(uint8_t out[SHA224_DIGEST_LENGTH], SHA256_CTX *ctx) {
- // SHA224_Init sets |ctx->md_len| to |SHA224_DIGEST_LENGTH|, so this has a
- // smaller output.
+ // This function must be paired with |SHA224_Init|, which sets |ctx->md_len|
+ // to |SHA224_DIGEST_LENGTH|.
assert(ctx->md_len == SHA224_DIGEST_LENGTH);
- return sha256_final_impl(out, ctx);
+ return sha256_final_impl(out, SHA224_DIGEST_LENGTH, ctx);
}
#ifndef SHA256_ASM
diff --git a/crypto/fipsmodule/sha/sha512.c b/crypto/fipsmodule/sha/sha512.c
index 708358e..2c7ce31 100644
--- a/crypto/fipsmodule/sha/sha512.c
+++ b/crypto/fipsmodule/sha/sha512.c
@@ -71,7 +71,7 @@
// this writing, so there is no need for a common collector/padding
// implementation yet.
-static int sha512_final_impl(uint8_t *out, SHA512_CTX *sha);
+static int sha512_final_impl(uint8_t *out, size_t md_len, SHA512_CTX *sha);
int SHA384_Init(SHA512_CTX *sha) {
sha->h[0] = UINT64_C(0xcbbb9d5dc1059ed8);
@@ -162,10 +162,10 @@
int SHA384_Final(uint8_t out[SHA384_DIGEST_LENGTH], SHA512_CTX *sha) {
- // |SHA384_Init| sets |sha->md_len| to |SHA384_DIGEST_LENGTH|, so this has a
- // smaller output.
+ // This function must be paired with |SHA384_Init|, which sets |sha->md_len|
+ // to |SHA384_DIGEST_LENGTH|.
assert(sha->md_len == SHA384_DIGEST_LENGTH);
- return sha512_final_impl(out, sha);
+ return sha512_final_impl(out, SHA384_DIGEST_LENGTH, sha);
}
int SHA384_Update(SHA512_CTX *sha, const void *data, size_t len) {
@@ -177,10 +177,10 @@
}
int SHA512_256_Final(uint8_t out[SHA512_256_DIGEST_LENGTH], SHA512_CTX *sha) {
- // |SHA512_256_Init| sets |sha->md_len| to |SHA512_256_DIGEST_LENGTH|, so this
- // has a |smaller output.
+ // This function must be paired with |SHA512_256_Init|, which sets
+ // |sha->md_len| to |SHA512_256_DIGEST_LENGTH|.
assert(sha->md_len == SHA512_256_DIGEST_LENGTH);
- return sha512_final_impl(out, sha);
+ return sha512_final_impl(out, SHA512_256_DIGEST_LENGTH, sha);
}
void SHA512_Transform(SHA512_CTX *c, const uint8_t block[SHA512_CBLOCK]) {
@@ -241,10 +241,10 @@
// |SHA512_Final| and expects |sha->md_len| to carry the size over.
//
// TODO(davidben): Add an assert and fix code to match them up.
- return sha512_final_impl(out, sha);
+ return sha512_final_impl(out, sha->md_len, sha);
}
-static int sha512_final_impl(uint8_t *out, SHA512_CTX *sha) {
+static int sha512_final_impl(uint8_t *out, size_t md_len, SHA512_CTX *sha) {
uint8_t *p = sha->p;
size_t n = sha->num;
@@ -268,8 +268,8 @@
return 0;
}
- assert(sha->md_len % 8 == 0);
- const size_t out_words = sha->md_len / 8;
+ assert(md_len % 8 == 0);
+ const size_t out_words = md_len / 8;
for (size_t i = 0; i < out_words; i++) {
CRYPTO_store_u64_be(out, sha->h[i]);
out += 8;