Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

From the RFC: "Its goal is to take some source of initial keying material and derive from it one or more cryptographically strong secret keys."

In our case, the initial keying material is the output of PBKDF; and the two outputs we use are used as an encryption key and a bearer token (essentially a password but I call it an authentication token to avoid confusion with your actual password). There are less complicated ways to do this; but this one is cryptographically conservative.

"essentially requires the server to be able to reverse HMAC-Hash to find the encryption key from the the authentication token" - the server can't do that; which is why the server can't figure out your encryption key from your authentication token. (The best the server could do would be to try a password guessing attack.)



Right what I'm confused about is that first bit, my understanding from the RFC is that the implementation should have look something like

    return pbkdf2.derive(password, email, PBKDF2_ROUNDS, STRETCHED_PASS_LENGTH_BYTES)
      .then((quickStretchedPW) => {
        result.quickStretchedPW = quickStretchedPW;
        // stretch to twice the length necessary
        return hkdf(quickStretchedPW, kw('generated'), HKDF_SALT, HKDF_LENGTH * 2)
          .then((generated) => {
            // split output into two cryptographically strong keys
            result.unwrapBkey = generated.slice(0, HKDF_LENGTH);
            result.authPW = generated.slice(HKDF_LENGTH);
          }
        );
      }
    )
but my read in pseudo code of what they end up doing is closer to this:

    hashed_password = hash(password, 'salt1')
    hashed_auth_tok = hash(hashed_password, 'salt2')
    hashed_unwrap_key = hash(hashed_password, 'salt3')
which seems secure because the server can't reverse hashed_unwrap_key to find hashed_password and thus shouldn't be able to calculate hashed_auth_tok. However the point of HKDF is to make multiple cryptographic keys while it looks like in practice we are just using it as a one way funciton.


Ah okay, I understand better.

The (second) pseudocode you have is right (the second two 'hash()' should be 'hkdf()', and the first should be 'pbkdf()'.)

The first is an alternate way to do it. But for cryptographic reasons that tend to be buried in formal proofs; you generally don't want to derive twice the keylength you need and then split for two keys. (Besides the necessity for formal proofs (as I understand it) - it's just easier to make an indexing mistake and reuse key material. One also becomes more vulnerable to a collision attack, although that might not make sense in this context it related to the formal proofs.) I will note that sometimes - especially in embedded spaces - you'll see people taking this shortcut in the name of speed or codesize.

Instead you want to fully derive two keys using separate HKDF calls with separate 'labels'. This provides strong domain separation for the keys.

But I'm mostly trying to provide with a pointer to what to read about to convince yourself. I'd start at https://crypto.stackexchange.com/search?q=domain+separation

If you find out we're doing something that still seems weird though, please send me an email!


got it, thanks for the response!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: