Skip to content

HKDF methods Expand and DeriveKey may generate corrupt output if 'info' and 'output' spans overlap #47009

@andreimilto

Description

@andreimilto

Description

User may supply overlapping spans as arguments info and output to the method HKDF.Expand. They may do so inadvertently or on purpose (e.g., if info will be no longer needed, they may decide to write the output directly into info to avoid unnecessary memory allocation for the output and reduce memory footprint). The method won't throw ArgumentException.

Expand writes output block-by-block, each block is generated by running the info through HMAC. If spans output and info overlap, writing one of the output blocks will modify the info and cause all the following output blocks (if any) to be invalid (computed for different info).

DeriveKey method uses Expand under the hood, so it is subjected to this problem too.

Illustration

Example 1:

byte[] prk = new byte[32];
byte[] info = new byte[64];
byte[] output = info;

HKDF.Expand(HashAlgorithmName.SHA256, prk, output, info);

// Expected value of 'output':
// 224, 85, 77, 187, 82, 30, 209, 96, 139, 180, 92, 98, 118, 111, 136, 68, 14, 47, 58, 228, 103, 215, 77, 186, 31, 211, 151, 244, 164, 46, 152, 253, 22, 124, 57, 137, 118, 144, 212, 164, 130, 145, 153, 172, 244, 110, 232, 164, 185, 232, 55, 182, 160, 145, 30, 122, 59, 192, 132, 49, 71, 152, 88, 179

// Actual value of 'output':
// 224, 85, 77, 187, 82, 30, 209, 96, 139, 180, 92, 98, 118, 111, 136, 68, 14, 47, 58, 228, 103, 215, 77, 186, 31, 211, 151, 244, 164, 46, 152, 253, 28, 152, 182, 90, 167, 151, 73, 167, 4, 223, 241, 200, 117, 212, 10, 138, 15, 233, 197, 158, 185, 124, 47, 216, 204, 204, 129, 232, 30, 186, 204, 65

Example 2:

var ikm = new byte[32];
var salt = new byte[32];

var buffer = new byte[128];
var info = buffer.AsSpan().Slice(0, 64);
var output = buffer.AsSpan().Slice(60, 64);

HKDF.DeriveKey(HashAlgorithmName.SHA256, ikm, output, salt, info);

// Expected value of 'output':
// 172, 123, 193, 152, 101, 162, 17, 66, 171, 216, 98, 225, 37, 223, 75, 218, 132, 136, 71, 80, 12, 22, 125, 64, 63, 27, 212, 255, 132, 8, 36, 105, 171, 126, 54, 101, 167, 164, 84, 70, 178, 14, 209, 132, 190, 173, 9, 22, 29, 248, 138, 190, 251, 103, 106, 197, 210, 193, 39, 96, 213, 148, 221, 10

// Actual value of 'output':
// 172, 123, 193, 152, 101, 162, 17, 66, 171, 216, 98, 225, 37, 223, 75, 218, 132, 136, 71, 80, 12, 22, 125, 64, 63, 27, 212, 255, 132, 8, 36, 105, 183, 96, 0, 94, 58, 127, 93, 192, 187, 170, 243, 216, 48, 149, 108, 144, 141, 5, 245, 16, 12, 186, 172, 48, 12, 194, 237, 63, 200, 37, 231, 109

Suggested Solution

Suggestion 1:
Check whether the spans info and output overlap, and if they do, throw exception:

if (output.Overlaps(info))
    throw new ArgumentException(...);

Suggestion 2:
Maybe it's a good idea to check all pairs of input and output spans in HKDF and throw exception if there's an overlap in any of them:

  • In Extract - ikm and prk, salt and prk
  • In Expand - prk and output, info and output
  • In DeriveKey - ikm and output, salt and output, info and output

This would make the API style more consistent and potentially safer, than if implementing only the info-output overlap check from the Suggestion 1.
I wasn't able to reproduce this issue with span pairs other than info-output, but I think it may be possible given different HMAC implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions