[noise] Analysis of Noise KDF

Trevor Perrin trevp at trevp.net
Fri Apr 29 10:09:49 PDT 2016


On Fri, Apr 29, 2016 at 2:51 AM, Jason A. Donenfeld <Jason at zx2c4.com> wrote:
>
> Here's specifically what I mean. Here is HKDF:
[...]

I see that that HKDF implementation uses an extra 33-byte buffer on
the stack, to concatenate the previous output and counter before
passing them to the next HMAC.

But that's not optimized - if your hash interface can process inputs
incrementally (most can), then you can just pass the previous output
and the counter separately, instead of concatenating them yourself.

(Internally most hash APIs keep their own buffer for an entire hash
block, and only run the compression function when the block is filled
or when input is finished).

If you're in an environment where a 33-byte stack buffer matters,
you're going to have to be optimizing a lot of things tightly, so this
doesn't seem like a big deal.

So I still don't think this outweighs the costs of being different from HKDF.

Trevor



>
> static void kdf(u8 *first_dst, u8 *second_dst, const u8 *data,
> size_t first_len, size_t second_len, size_t data_len,
> const u8 chaining_key[NOISE_HASH_LEN])
> {
> u8 secret[BLAKE2S_OUTBYTES];
> u8 output[BLAKE2S_OUTBYTES + 1];
> BUG_ON(first_len > BLAKE2S_OUTBYTES || second_len > BLAKE2S_OUTBYTES);
>
> /* Extract entropy from data into secret */
> blake2s_hmac(secret, data, chaining_key, BLAKE2S_OUTBYTES, data_len,
> NOISE_HASH_LEN);
>
> /* Expand first key: key = secret, data = 0x1 */
> output[0] = 1;
> blake2s_hmac(output, output, secret, BLAKE2S_OUTBYTES, 1, BLAKE2S_OUTBYTES);
> memcpy(first_dst, output, first_len);
>
> /* Expand second key: key = secret, data = first-key || 0x2 */
> output[BLAKE2S_OUTBYTES] = 2;
> blake2s_hmac(output, output, secret, BLAKE2S_OUTBYTES, BLAKE2S_OUTBYTES + 1,
> BLAKE2S_OUTBYTES);
> memcpy(second_dst, output, second_len);
>
> /* Clear sensitive data from stack */
> memzero_explicit(secret, BLAKE2S_OUTBYTES);
> memzero_explicit(output, BLAKE2S_OUTBYTES + 1);
> }
>
> And here is the more simpler alternative I suggested:
>
> static void kdf(u8 *first_dst, u8 *second_dst, const u8 *data,
> size_t first_len, size_t second_len, size_t data_len,
> const u8 chaining_key[NOISE_HASH_LEN])
> {
> u8 secret[BLAKE2S_OUTBYTES];
> BUG_ON(first_len > BLAKE2S_OUTBYTES || second_len > BLAKE2S_OUTBYTES);
>
> /* Extract entropy from data into secret */
> blake2s_hmac(secret, data, chaining_key, BLAKE2S_OUTBYTES, data_len,
> NOISE_HASH_LEN);
>
> /* Expand first key: key = secret, data = [empty] */
> blake2s_hmac(first_dst, NULL, secret, BLAKE2S_OUTBYTES, 0,
> BLAKE2S_OUTBYTES);
>
> /* Expand second key: key = secret, data = first-key */
> blake2s_hmac(second_dst, first_dst, secret, BLAKE2S_OUTBYTES,
> BLAKE2S_OUTBYTES, BLAKE2S_OUTBYTES);
>
> /* Clear sensitive data from stack */
> memzero_explicit(secret, BLAKE2S_OUTBYTES);
> }
>
> Less stack, less copying --> faster, simpler.


More information about the Noise mailing list