HKDF-Expand implementation in c#

276 views
Skip to first unread message

Kratos

unread,
May 26, 2014, 7:22:59 PM5/26/14
to rncr...@googlegroups.com
Please, I would like your input on the following HKDF-Expand implementation in c# for v4.

My plan is to use Rfc2898DeriveBytes class to get (extract) the Pseudorandom key (prk). Then I will used the below method to expand the prk to get others keys.


public byte[] ExpandPrk(byte[] prk, HMAC hmac, byte[] info, int length)
        {
            if (hmac == null) hmac = new HMACSHA512(); // if hmac is null  then hmac will be initialized to a new instance of HMACSHA512            
            if (info == null) info = new byte[0]; // if info is null then it will initialized the variable with zeros
            if (length == null) length = 32; // if length is null then it will initialized with the number 32(256 bits) which will be the length of opm

            uint hashLength = (uint) hmac.HashSize / 8; // based on output of selected hash 
            hmac.Key = prk;

            int n = (int)System.Math.Ceiling((Single)length / hashLength); // N = ceil(L/HashLen) number of loops needed to get the required length
            byte[] tn = new byte[n * hashLength];
            
            using (MemoryStream ms = new System.IO.MemoryStream())
            {
                byte[] tempBuffer = new byte[0]; // tempBuffer will keep the bytes from the previous (prev) computed hash which
                                                // will be used with the next hash computation

                for (int i = 1; i <= n; i++)
                {
                    ms.Write(tempBuffer, 0, tempBuffer.Length);
                    if (info.Length > 0) ms.Write(info, 0, info.Length);
                    ms.WriteByte((byte)(0x01 * i)); // adds i information to the info data

                    tempBuffer = hmac.ComputeHash(ms.ToArray());

                    Array.Copy(tempBuffer, 0, tn, (i - 1) * hashLength, hashLength); // keeps adding to tn until requested length is satisfied

                    ms.SetLength(0); //reset stream
                }
            }

            byte[] okm = new byte[length]; // okm  = output key material
            Array.Copy(tn, okm, okm.Length); // copy only the requested size as result

            return okm;     // okm now contains the new key with requested length
        }

Rob Napier

unread,
May 28, 2014, 9:56:37 AM5/28/14
to rncr...@googlegroups.com
On Monday, May 26, 2014 7:22:59 PM UTC-4, Kratos wrote:
Please, I would like your input on the following HKDF-Expand implementation in c# for v4.

My plan is to use Rfc2898DeriveBytes class to get (extract) the Pseudorandom key (prk). Then I will used the below method to expand the prk to get others keys.

Rfc2989DeriveBytes uses PBKDF2 with SHA-1 The v4 spec calls for PBKDF2 with SHA-512. Does .NET have a PBKDF2 function that accepts a PRF? Note that I would rather use SHA-1 here and be able to use standard platform tools than to use SHA-512 and have to write a custom PBKDF2. There is absolutely no security concern with using SHA-1 here; I chose SHA-512 because I use it elsewhere, and I wanted to minimize the number of primitives required.

If you can research whether there is an easy way to get PBKDF2+SHA512 in .NET, that would be very useful information.
 
Regarding the code below, I assume the looping is in order to be very general? The v4 format should always be able to compute the HMAC in one iteration. I'm not sure why a MemoryStream is necessary here. It seems a lot of complexity over a single call to ComputeHash(Byte[]). I'd be very tempted to simplify this code and simply assert that length<=HashSize, and it is a programming error to request otherwise.

Is there any reason to pas the HMAC object here? Again, my recommendation is to keep things simple so that they are easy to reason about and test. I encourage simplicity and correctness over flexibility in the implementation.

Thanks for the code; I really do need to get back to work on v4 myself.

-Rob

Kratos

unread,
Jun 6, 2014, 11:16:03 PM6/6/14
to rncr...@googlegroups.com
Rob,

I think I found a library which is able to implement PBKDF2+SHA512 in C# at "https://www.nuget.org/packages/PBKDF2.NET/"


Thanks,

Kratos

Rob Napier

unread,
Jun 16, 2014, 10:31:20 AM6/16/14
to rncr...@googlegroups.com
Thanks for the research. Wherever possible, I strongly prefer using the built-in crypto libraries. Failing that, I look for a single, well-established crypto library that includes all the required primitives (for example, SJCL in JavaScript). Unless absolutely necessary, I avoid ad hoc crypto routines, particularly if it isn't from an established crypto developer. It's just too easy to get something wrong.

I admit that it's hard to envision how a PBKDF2 or hash implementation could screw things up as long as it's final output matches other, better known implementations. So I'll need to think more on this. I just have a strong aversion to adding any security code that hasn't gone through some expert review. I'll give it some more thought, since I expect to get some concern from people over using SHA-1 here, and it'd be nice to alleviate that concern even if it's mistaken. (NIST 800-132 still lists SHA-1 as its PRF. FIPS-180-4 still approves SHA-1.)

Thanks,
-Rob
Reply all
Reply to author
Forward
0 new messages