Take the 2-minute tour ×
Cryptography Stack Exchange is a question and answer site for software developers, mathematicians and others interested in cryptography. It's 100% free, no registration required.

Use the PBKDF2 output as an HMAC key, hashing two different inputs - once for 20 bytes and once for 12 :

using (var PBKDF2 = new Rfc2898DeriveBytes(passwordBytes, salt, 100000))
{
    HMACSHA1 hmac = new HMACSHA1(PBKDF2.GetBytes(20));
    hmac.Initialize();
    byte[] aesKey = hmac.ComputeHash(new byte[] { 1, 0, 1 })
        .Concat(hmac.ComputeHash(new byte[] { 1, 1, 1 })).Take(12).ToArray();
}

Or perform PBKDF2 twice, splitting the iterations, and using 2 different salts :

byte[] aes2;
using (var PBKDF2 = new Rfc2898DeriveBytes(passwordBytes, salt1, 50000))
    aes2 = PBKDF2.GetBytes(20);
using (var PBKDF2 = new Rfc2898DeriveBytes(passwordBytes, salt2, 50000))
    aes2 = aes2.Concat(PBKDF2.GetBytes(12)).ToArray();

References: Rfc2898DeriveBytes , HMACSHA1 .

share|improve this question
    
I prefer something similar to the former, but following the HKDF-Expand spec. Second one is no better than calling PBKDF2.GetBytes(32) directly. –  CodesInChaos Jan 30 at 13:06
    
If you just want to use a single primitive (ie, PBKDF2), I'd recommend reading D.W's excellent answer here: crypto.stackexchange.com/questions/8232/… –  hunter Jan 30 at 13:33
    
One simple workaround is deriving a 20 byte master key with expensive PBKDF2, and then applying PBKDF2 with 1 iteration and long output to it. As another simple technique applicable when you only need 32-64 bytes, you can simply hash the output of PBKDF2 once with SHA256 or SHA512 to expand it. –  CodesInChaos Jan 30 at 13:58
2  
@CodesInChaos Thanks. You might as well turn these comments (and to my previous question) into answers so I can upvote them. –  ispiro Jan 30 at 14:13
1  
@CodesInChaos Making sure I understand you correctly: your suggestion of using PBKDF2 twice means: feeding the 20 byte output as the "password" for the single iteration run (and reusing the original salt). Is that it? About your second suggestion of simply hashing the result: Now I'm really confused - if it's that simple (since we're not getting any more entropy than our password+salt anyway) - why did they invent HKDF-Expand at all? –  ispiro Jan 30 at 14:13

2 Answers 2

It would help to understand the context a little. I'm assuming you're trying to arrive at a password-based key and you're trying to maximize the brute-force time required to guess the key. But maybe this data will be used as a tag, or you're interested in maximizing the time to create a password-key dictionary or something.

I would consider the search space an attacker would have to go through in making your decision. The first one has a search space of $|passwordBytes|\times|salt|$. The second has a search space of $|passwordBytes|\times 2\times|salt|$, so requires $O(2^{|salt|})$ more computation. Doubling the iteration count just doubles the computation (i.e. adds one bit of complexity). The time to compute a dictionary would be higher for your second option.

However, it would actually be easier to say something about just the first 20 or just the last 12 bytes in the second scheme (since it uses a lower iteration count), which could allow brute-forcing the other half even more trivially. To prevent this, you'd do better to follow CodesInChaos's suggestion and cascade operations, but make sure the final output is a single output from a secure primitive. For instance, you might use $tmp = PBKDF2(passwordBytes, salt1, 50000)$ to get $|passwordBytes|$ of data, then use $SHA256(PBKDF2(tmp, salt2, 50000))$ (or even just $SHA256(tmp || salt2)$) to get 32 bytes. This makes the search space as big as $|passwordBytes|\times 2\times|salt|$ but doesn't leak information from just one or the other halves of the output.

In any case, depending on how you're using the data, I suspect some other primitive (like SHA-256) might be useful for you.

share|improve this answer
    
Thanks. I understand the need for the final output to be a single output so that my second option is not so good. But why did you add the salt in the last step? Wouldn't $SHA256(tmp)$ be just as good? –  ispiro Jan 30 at 19:53
    
@ispiro: Just to increase the search space over 2*|salt|, if that happened to be important and not too cumbersome. Dealing with salt correctly can be quite a pain, but it's also a working way to make brute-force/dictionary attacks much harder. –  jspencer Jan 30 at 20:02

There are multiple ways to do this, but I recommend neither of the solution proposals in the question.

Instead you can, for instance, Use SHA-1 with PBKDF2, use KBKDF after PBKDF2, use authenticated en/decryption after PBKDF2, request longer output from PBKDF2. Depending on your needs, any one of these could be the solution for you. For details, read on.

Using KBKDF after PBKDF2 is similar to your suggestion hash twice, but "better cryptographic hygiene", if doing key derivation, use should use a well-defined key derivation operation.

Similarly, requesting large than one block output from PBKDF2 is actually similar to your suggestion of performing PBKDF2 twice (as it ends up doing twice as much work) than deriving 160-bit value using PBKDF2 and HMAC-SHA2.

Short Introduction to PBKDF2

PBKDF2 is a function that is based on a PRF (pseudo random function).

Pseudo-Random functions accepted by PBKDF2 vary according to implementation. RFC #2898 specified HMAC-MD5 and HMAC-SHA1. Many PBKDF2 implementations, use HMA-SHA1, and many of the test vectors available are using SHA1 (like RFC 6070).

However, PBKDF2 is not tied to HMAC-SHA1. Newer versions of specification (such as NIST SP 800-132) allow any HMAC based on any NIST approved hash (SHA-1 or SHA-2 family, such as SHA-256 or SHA-512).

Entropy

Using HMAC-SHA-1 with PBKDF2 puts 160-bit cap for the entropy of the password. If you want to retain entropy of high-entropy passwords (several tens of random characters), then this will become an issue. Thus, if you need 256-bit secret with full entropy, then you MUST not use SHA-1 and you must choose SHA-256.

Solutions

The solutions for using PBKDF2 to derive 256-bit secret are relatively straight forwards explained in NIST SP 800-132.

  1. Use HMAC-SHA-256 with PBKDF2 (may require different implementation of PBKDF2). Note: this will better retain entropy of high-entropy passwords as explained above.
  2. Use PBKDF2 to create up-to 160 bit secret that is used with key derivation (for instance NIST SP 800-108, KBKDF) or key wrapping (for instance NIST SP 800-38F, AES-KW) to derive or decrypt the 256-bit secret.
  3. Use PBKDF2 algorithm allows larger output than the size of hash. Thus you can use PBKDF2 (with HMAC-SHA1) to derive 256-bit value (uses twice as much time than above item #2) and does not provide more security against key brute-forcing attacks.

It is most important to acknowledge the each one of these choices will result in different keys. Thus, for interoperability concerns it may be that you are constrained in what option(s) you can choose.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.