-
Notifications
You must be signed in to change notification settings - Fork 134
Implement HMAC over SHA3 truncated variants #2484
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
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2484 +/- ##
==========================================
- Coverage 78.91% 78.87% -0.05%
==========================================
Files 622 640 +18
Lines 108849 109638 +789
Branches 15421 15528 +107
==========================================
+ Hits 85902 86475 +573
- Misses 22276 22466 +190
- Partials 671 697 +26 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
8513c0a
to
cda6444
Compare
EXPECT_EQ(Bytes(output), Bytes(mac.get(), mac_len)); | ||
OPENSSL_memset(mac.get(), 0, expected_mac_len); // Clear the prior correct answer | ||
|
||
// Test that an HMAC_CTX may be reset with the same key and a null md |
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.
Why did you move these tests? Did you change anything 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.
I moved them here to retain full coverage on all HMAC variants, trying to minimize duplication/redundancy in TestVectorsPrecomputedKey
. I changed the comments, but the test code changes here are strictly code motion -- teasing out precomputed-specific cases and copying some portions of the test for setup and coverage.
FIPS_service_indicator_update_state(); | ||
return 1; | ||
} | ||
|
||
int SHA3_224_Init(KECCAK1600_CTX *ctx) { |
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.
We already have all of these in digest.c, can you just update those to not be static?
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 don't think so. The signatures are different (KECCAK1600_CTX
vs. EVP_MD_CTX
), so digests.c
has those static wrappers to wrap SHA1 and SHA2's ctx-specific public functions. For SHA3, we follow the same pattern (e.g. SHA512_256_Update
also has a corresponding static function in digests.c
), only the ctx-specific functions here aren't exposed in public headers.
@@ -181,7 +181,7 @@ OPENSSL_EXPORT int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, | |||
|
|||
// EVP_MAX_MD_BLOCK_SIZE is the largest digest block size supported, in | |||
// bytes. | |||
#define EVP_MAX_MD_BLOCK_SIZE 128 // SHA-512 is the longest so far. | |||
#define EVP_MAX_MD_BLOCK_SIZE 144 // SHA3-224 has the largest block size so far |
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.
Does anything check/enforce this?
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.
Indirectly, for digests used with HMAC. It's checked here at compile time.
out->methods[idx-1].get_state = \ | ||
AWS_LC_TRAMPOLINE_##HASH_NAME##_get_state; \ | ||
} | ||
|
||
DEFINE_LOCAL_DATA(struct hmac_method_array_st, AWSLC_hmac_in_place_methods) { | ||
OPENSSL_memset((void*) out->methods, 0, sizeof(out->methods)); | ||
int idx = 0; | ||
// Since we search these linearly it helps (just a bit) to put the most common ones first. | ||
// This isn't based on hard metrics and will not make a significant different on performance. | ||
// FIXME: all hashes but SHA256 have been disabled to check first SHA256 |
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.
Do you know what this FIXME is referencing?
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 do not. It was added with the pre-computed keys change. Do you know @fabrice102?
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.
My mistake. It should be removed. It was for an intermediary commit to show how the changes would look like. But this is no more true.
crypto/fipsmodule/hmac/hmac.c
Outdated
// For merkle-daamgard constructions, we also define functions for manipulating | ||
// hash state |h| to calculate or use precomputed keys. These are not applicable | ||
// to Keccak/SHA3. |
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.
Is that not allowed or just not implemented (yet)?
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 internal hash state is not readily exportable in SHA3/Keccak in the way that it is in merkle-damgard, so these functions are not applicable. I'll clarify the comment.
@@ -250,12 +250,17 @@ OPENSSL_EXPORT int HMAC_CTX_copy(HMAC_CTX *dest, const HMAC_CTX *src); | |||
// Private functions | |||
typedef struct hmac_methods_st HmacMethods; | |||
|
|||
// We use a union to ensure that enough space is allocated and never actually bother with the named members. | |||
// We use a union to ensure that enough space is allocated and never actually | |||
// bother with the named members. We do not externalize SHA3 ctx definition, |
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.
Should we mention that SHA3 ctx is referring to KECCAK1600_CTX
here? It doesn't seem immediately obvious.
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've intentionally kept mentions of KECCAK1600_CTX
out of the public headers. Developers will be able to read explanatory comments in sha/internal.h
to figure out what's going on.
union md_ctx_union { | ||
MD5_CTX md5; | ||
SHA_CTX sha1; | ||
SHA256_CTX sha256; | ||
SHA512_CTX sha512; | ||
uint8_t sha3[400]; | ||
}; |
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.
A bit of a bolder change, but could we move the md_ctx_union
union to our internal headers? I don't see any existing OpenSSL reference to the union. That way we could abstract 400
to a #define
variable instead of a hard-coded number.
If we're not comfortable doing so, we can leave this as is though.
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 agree that it would be preferable to keep this internal and make HMAC_CTX
fully opaque, but it's in our public headers today, so moving it has the potential to break downstream builds. I'd rather not make a breaking change unless we need to.
@@ -181,7 +181,7 @@ OPENSSL_EXPORT int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data, | |||
|
|||
// EVP_MAX_MD_BLOCK_SIZE is the largest digest block size supported, in | |||
// bytes. | |||
#define EVP_MAX_MD_BLOCK_SIZE 128 // SHA-512 is the longest so far. | |||
#define EVP_MAX_MD_BLOCK_SIZE 144 // SHA3-224 has the largest block size so far |
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.
Had to cross reference the math being done in crypto/fipsmodule/sha/internal.h
. Seems a bit magical that SHA3-224 has the largest block size now.
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.
SHA3's block size reduces as its output size grows. I would link to that SP here, but it's not applicable to all digests -- just those approved for HMAC by NIST.
This looks good to me. Minor comment:
Technically, I think we could do the same and support pre-computed keys by exporting the state after absorbing the HMAC key. However, as mentioned by @WillChilds-Klein in #2484 (comment), exporting the state is not readily available. Furthermore, I don't think there is a good use case to support pre-computed keys for HMAC-SHA-3. If there ever is in the future, it would not be difficult to add this support and still be backward compatible. So I agree that we should not be supporting HMAC-SHA-3 pre-computed keys. |
Description
This PR implements HMAC over truncated SHA3 variants as specified in NIST SP 800-224. We do so without externalizing any SHA3/keccak internals, including its context struct. This is done intentionally, as OpenSSL has not yet externalized its SHA3/keccak context struct and we want to leave the door open to future interoperability. To work around this in
hmac.h
'smd_ctx_union
, we hard-code the context struct size and add a compile-time assertion that it does not grow larger than the hard-coded value.SHA3 is unique in supported HMAC digests in that, due to differences between its sponge construction 8000 and others' Merkle-Dåmgard constructions, it does not support pre-computed keys. To accommodate this difference, we refactor the relevant code generation macros and relevant unit tests.
The HMAC-SHA3 feature gap was discovered in a somewhat roundabout way. While preparing a pull request to add AWS-LC to upstream CPython's CI, I discovered that CPython's
./configure
script's compile probe failed to detectlibcrypto
support for linkinghashlib
. The compile probe referencedNID_blake2b512
, which AWS-LC does not support. The consequence of this was that CPython used its HACL implementations forhashlib
instead of linking AWS-LC. This did not affect ourssl
integration, as AWS-LC always uses its own hash functions for TLS.Call-outs
KAT vectors in
hmac_tests.txt
were calculated using a simple python (linked against system OpenSSL, not AWS-LC) session like so:Testing
.txt
, added them to unit testshashlib
against AWS-LCBy submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.