dotnet / dotnet/runtime

HKDF.DeriveKey mutates ikm parameter and returns all-zero output in .NET 10

Open
#130,578 3 comments 0 reactions 1 assignee Claimed by @vcsjones View on GitHub
area-System.Security needs-author-action
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

## Description
In .NET 10, HKDF.DeriveKey exhibits two critical bugs:

The ikm byte array is mutated after the call — even when using the ReadOnlySpan overload, the underlying array content is overwritten.
The output is always all zeros — regardless of input values, DeriveKey produces zero-filled output.
HKDF.Extract and HKDF.Expand work correctly when called individually. The bug is specific to HKDF.DeriveKey.

## Reproduction

C#
```
using System.Security.Cryptography;

// RFC 5869 Appendix A.1 test vector
byte[] ikm = new byte[] {
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
};
byte[] salt = new byte[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c
};
byte[] info = new byte[] {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9
};

byte[] ikmCopy = new byte[ikm.Length];
Buffer.BlockCopy(ikm, 0, ikmCopy, 0, ikm.Length);
byte[] output = new byte[42];

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

Console.WriteLine($"ikm mutated: {!ikmCopy.AsSpan().SequenceEqual(ikm)}");
// Output: ikm mutated: True
// ikm changed from: 0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B-0B
// to: E4-F4-90-BD-DC-B7-26-F9-8D-D2-D4-2F-F2-98-80-33-BB-E3-AC-E4-56-61

Console.WriteLine($"output is all zeros: {output.All(b => b == 0)}");
// Output: output is all zeros: True

// Expected OKM (RFC 5869 A.1, 42 bytes):
// 3C-B2-5F-25-FA-AC-D5-7A-90-43-4F-64-D0-36-2F-2A-2D-2D-0A-90-CF-1A-5A-4C-5D-B0-2D-56-EC-C4-C5-BF-34-00-72-08-D5-B8-87-18-58-65
```

The ReadOnlySpan overload exhibits the same behavior:

C#
```
HKDF.DeriveKey(HashAlgorithmName.SHA256, output.AsSpan(), ikmCopy.AsSpan(), salt.AsSpan(), info.AsSpan());
// ikmCopy is still mutated, output is still all zeros
```

## What works correctly

HKDF.Extract(HashAlgorithmName.SHA256, ikm, salt) — produces correct PRK, does not mutate ikm
HKDF.Expand(HashAlgorithmName.SHA256, prk, output, info) — produces correct OKM, does not mutate prk
Combining HKDF.Extract + manual HKDF.Expand (or using HMACSHA256 directly) works correctly as a workaround.

## Affected parameters
The bug reproduces with all ikm lengths tested (16, 22, 32, 64 bytes) and various salt/info sizes. It is not specific to any particular input size.

## Configuration
.NET version: 10.0.9
CLR: 10.0.9
OS: Windows 11 (x64)
Runtime: net10.0
Regression
This is a regression from .NET 9 and earlier, where HKDF.DeriveKey worked correctly and matched RFC 5869.

## Impact
This is a cryptographic correctness bug:

Key derivation produces deterministic all-zero output, which is trivially predictable
Anyone relying on HKDF.DeriveKey for key material derivation will get zero keys, completely breaking security
The ikm mutation is a side-channel concern and violates the ReadOnlySpan contract

## Workaround
Use HKDF.Extract + HKDF.Expand separately, or a manual HMAC-SHA256 implementation of RFC 5869.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.