From: Michael Brown Date: Sat, 20 Jun 2026 09:00:44 +0000 (+0100) Subject: [crypto] Allow for input keying material to overlap output X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a2eba23aceb2a05ddb96c3661596784fb1698b96;p=thirdparty%2Fipxe.git [crypto] Allow for input keying material to overlap output Calling hkdf_extract() with no salt and with the input keying material provided in the same buffer that will hold the output pseudorandom key is a valid potential use case. This will currently fail silently since the input keying material would be overwritten by the constructed all-zero salt before being consumed. Fix by using a local buffer for the all-zero salt, rather than constructing the salt in the output buffer. Document the permitted behaviour in terms of overlapping input and output buffers for both hkdf_extract() and hkdf_expand(), and extend the test cases to verify this behaviour. Signed-off-by: Michael Brown --- diff --git a/src/crypto/hkdf.c b/src/crypto/hkdf.c index 97f856df3..291eafe64 100644 --- a/src/crypto/hkdf.c +++ b/src/crypto/hkdf.c @@ -49,18 +49,22 @@ FILE_SECBOOT ( PERMITTED ); * @v ikm Input keying material * @v ikm_len Length of input keying material * @v prk Pseudorandom key to fill in + * + * The salt and input keying material are allowed to overlap the + * buffer that will be filled in with the pseudorandom key. */ void hkdf_extract ( struct digest_algorithm *digest, const void *salt, size_t salt_len, const void *ikm, size_t ikm_len, void *prk ) { uint8_t ctx[ hmac_ctxsize ( digest ) ]; + uint8_t zero[ digest->digestsize ]; /* Use all-zero salt if not specified */ if ( ! salt ) { assert ( salt_len == 0 ); - salt_len = digest->digestsize; - memset ( prk, 0, salt_len ); - salt = prk; + memset ( zero, 0, sizeof ( zero ) ); + salt = zero; + salt_len = sizeof ( zero ); } /* Calculate pseudorandom key */ @@ -78,6 +82,15 @@ void hkdf_extract ( struct digest_algorithm *digest, const void *salt, * @v info_len Length of additional information * @v out Output keying material * @v len Length of output keying material + * + * The pseudorandom key and additional information are allowed to + * overlap the buffer that will be filled in with the output keying + * material, provided that the length of the output keying material is + * less than or equal to the length of the pseudorandom key. + * + * In particular, this allows hkdf_expand() to be used to create a new + * generation of pseudorandom key (i.e. to have the output keying + * material overwrite the existing pseudorandom key). */ void hkdf_expand ( struct digest_algorithm *digest, const void *prk, const void *info, size_t info_len, void *out, size_t len ) { diff --git a/src/tests/hkdf_test.c b/src/tests/hkdf_test.c index 95b19d10b..9acfa828a 100644 --- a/src/tests/hkdf_test.c +++ b/src/tests/hkdf_test.c @@ -123,8 +123,16 @@ struct hkdf_test { static void hkdf_okx ( struct hkdf_test *test, const char *file, unsigned int line ) { size_t digestsize = test->digest->digestsize; + union { + uint8_t ikm[test->ikm_len]; + uint8_t salt[test->salt_len]; + uint8_t info[test->info_len]; + uint8_t prk[test->prk_len]; + uint8_t okm[test->okm_len]; + } overlap; uint8_t prk[digestsize]; uint8_t okm[test->okm_len]; + size_t check_len; /* Sanity checks */ okx ( ( test->salt != NULL ) || ( test->salt_len == 0 ), file, line ); @@ -137,8 +145,41 @@ static void hkdf_okx ( struct hkdf_test *test, const char *file, /* Test expansion */ hkdf_expand ( test->digest, test->prk, test->info, test->info_len, - okm, sizeof ( okm ) ); + okm, test->okm_len ); okx ( memcmp ( okm, test->okm, test->okm_len ) == 0, file, line ); + + /* Test overlap between salt and pseudorandom key */ + if ( test->salt ) { + memcpy ( overlap.salt, test->salt, test->salt_len ); + hkdf_extract ( test->digest, overlap.salt, test->salt_len, + test->ikm, test->ikm_len, overlap.prk ); + okx ( memcmp ( overlap.prk, test->prk, digestsize ) == 0, + file, line ); + } + + /* Test overlap between input keying material and pseudorandom key */ + memcpy ( overlap.ikm, test->ikm, test->ikm_len ); + hkdf_extract ( test->digest, test->salt, test->salt_len, overlap.ikm, + test->ikm_len, overlap.prk ); + okx ( memcmp ( overlap.prk, test->prk, digestsize ) == 0, + file, line ); + + /* Calculate length for expansion overlap tests */ + check_len = test->okm_len; + if ( check_len > digestsize ) + check_len = digestsize; + + /* Test overlap between pseudorandom key and output */ + memcpy ( overlap.prk, test->prk, test->prk_len ); + hkdf_expand ( test->digest, overlap.prk, test->info, test->info_len, + overlap.okm, test->okm_len ); + okx ( memcmp ( overlap.okm, test->okm, check_len ) == 0, file, line ); + + /* Test overlap between additional information and output */ + memcpy ( overlap.info, test->info, test->info_len ); + hkdf_expand ( test->digest, test->prk, overlap.info, test->info_len, + overlap.okm, test->okm_len ); + okx ( memcmp ( overlap.okm, test->okm, check_len ) == 0, file, line ); } #define hkdf_ok( test ) hkdf_okx ( test, __FILE__, __LINE__ )