Instantly share code, notes, and snippets.

Embed
What would you like to do?
HKDF in C#
class Hkdf
{
Func<byte[],byte[],byte[]> keyedHash;
public Hkdf()
{
var hmac = new HMACSHA256();
keyedHash = (key, message)=>
{
hmac.Key=key;
return hmac.ComputeHash(message);
};
}
public byte[] Extract(byte[] salt, byte[] inputKeyMaterial)
{
return keyedHash(salt, inputKeyMaterial);
}
public byte[] Expand(byte[] prk, byte[] info, int outputLength)
{
var resultBlock = new byte[0];
var result = new byte[outputLength];
var bytesRemaining = outputLength;
for (int i = 1; bytesRemaining > 0; i++)
{
var currentInfo = new byte[resultBlock.Length + info.Length + 1];
Array.Copy(resultBlock, 0, currentInfo, 0, resultBlock.Length);
Array.Copy(info, 0, currentInfo, resultBlock.Length, info.Length);
currentInfo[currentInfo.Length - 1] = (byte)i;
resultBlock = keyedHash(prk, currentInfo);
Array.Copy(resultBlock, 0, result, outputLength - bytesRemaining, Math.Min(resultBlock.Length, bytesRemaining));
bytesRemaining -= resultBlock.Length;
}
return result;
}
public byte[] DeriveKey(byte[] salt, byte[] inputKeyMaterial, byte[] info, int outputLength)
{
var prk = Extract(salt, inputKeyMaterial);
var result = Expand(prk, info, outputLength);
return result;
}
}
var hkdf = new Hkdf();
{
var ikm = StringToByteArray("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
var salt = StringToByteArray("000102030405060708090a0b0c");
var info = StringToByteArray("f0f1f2f3f4f5f6f7f8f9");
var L = 42;
var prk = "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5";
var okm = "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865";
var actualPrk = ByteArrayToString(hkdf.Extract(salt, ikm));
var actualOkm = ByteArrayToString(hkdf.DeriveKey(salt, ikm, info, L));
(prk == actualPrk).Dump();
(okm == actualOkm).Dump();
}
{
var ikm = StringToByteArray("000102030405060708090a0b0c0d0e0f"+
"101112131415161718191a1b1c1d1e1f"+
"202122232425262728292a2b2c2d2e2f"+
"303132333435363738393a3b3c3d3e3f"+
"404142434445464748494a4b4c4d4e4f");
var salt = StringToByteArray("606162636465666768696a6b6c6d6e6f"+
"707172737475767778797a7b7c7d7e7f"+
"808182838485868788898a8b8c8d8e8f"+
"909192939495969798999a9b9c9d9e9f"+
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf");
var info = StringToByteArray("b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"+
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"+
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"+
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"+
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
var L = 82;
var prk = "06a6b88c5853361a06104c9ceb35b45cef760014904671014a193f40c15fc244";
var okm = "b11e398dc80327a1c8e7f78c596a4934"+
"4f012eda2d4efad8a050cc4c19afa97c"+
"59045a99cac7827271cb41c65e590e09"+
"da3275600c2f09b8367793a9aca3db71"+
"cc30c58179ec3e87c14c01d5c1f3434f"+
"1d87";
var actualPrk = ByteArrayToString(hkdf.Extract(salt, ikm));
var actualOkm = ByteArrayToString(hkdf.DeriveKey(salt, ikm, info, L));
(prk == actualPrk).Dump();
(okm == actualOkm).Dump();
}
{
var ikm = StringToByteArray("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
var salt = new byte[0];
var info = new byte[0];
var L = 42;
var prk = "19ef24a32c717b167f33a91d6f648bdf96596776afdb6377ac434c1c293ccb04";
var okm = "8da4e775a563c18f715f802a063c5a31"+
"b8a11f5c5ee1879ec3454e5f3c738d2d"+
"9d201395faa4b61a96c8";
var actualPrk = ByteArrayToString(hkdf.Extract(salt, ikm));
var actualOkm = ByteArrayToString(hkdf.DeriveKey(salt, ikm, info, L));
(prk == actualPrk).Dump();
(okm == actualOkm).Dump();
}
@rclabo

This comment has been minimized.

Show comment
Hide comment
@rclabo

rclabo Feb 17, 2017

Thank you for sharing your code. In the end I decided to write my own HKDF class rather than using yours so that I would more fully understand the https://tools.ietf.org/html/rfc5869 spec and how HKDF works. But seeing that your code was so short definitely encouraged me to try my hand at it and actually read the spec. Thank you.

rclabo commented Feb 17, 2017

Thank you for sharing your code. In the end I decided to write my own HKDF class rather than using yours so that I would more fully understand the https://tools.ietf.org/html/rfc5869 spec and how HKDF works. But seeing that your code was so short definitely encouraged me to try my hand at it and actually read the spec. Thank you.

@rclabo

This comment has been minimized.

Show comment
Hide comment
@rclabo

rclabo Feb 23, 2017

CodesInChaos - Now that I realize you are active on crypto.stackexchange.com and have have a strong reputation, I should have just used this gist you provided for HKDF rather than creating my own. But at the time I didn't realize it, and I guess it was a great learning experience for me that was good. The only thing you might want to change in your code is to use a using statement in var hmac = new HMACSHA256(); since HMACSHA256 implements IDisposable. Personally I wish the rfc provided more test vectors, three just feels so skimpy but I couldn't find any more.

rclabo commented Feb 23, 2017

CodesInChaos - Now that I realize you are active on crypto.stackexchange.com and have have a strong reputation, I should have just used this gist you provided for HKDF rather than creating my own. But at the time I didn't realize it, and I guess it was a great learning experience for me that was good. The only thing you might want to change in your code is to use a using statement in var hmac = new HMACSHA256(); since HMACSHA256 implements IDisposable. Personally I wish the rfc provided more test vectors, three just feels so skimpy but I couldn't find any more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment