Created
January 30, 2014 14:57
-
-
Save CodesInChaos/8710228 to your computer and use it in GitHub Desktop.
HKDF in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 invar hmac = new HMACSHA256();
sinceHMACSHA256
implementsIDisposable
. Personally I wish the rfc provided more test vectors, three just feels so skimpy but I couldn't find any more.