Search code examples
node.jscryptographybufferlibsodium

What am I misunderstanding about password hashing?


It is my understanding that a hash function will always return the same results when fed the same data. But I've been using libsodium (via node-sodium) and that is not what is happening.

I have this in my schema:

UserSchema.pre('save', function(next) {
    // declare my variables       
    let user = this,
        buf = Buffer.alloc(sodium.crypto_pwhash_STRBYTES, 'ascii'),
        passwordBuf = Buffer.from(user.password, 'ascii'),
        saltedPassBuf,
        hash;
    // only hash the password if it has been modified (or is new)
    if (!user.isModified('password')) return next();
    // generate a salt
    sodium.randombytes_buf(buf, sodium.crypto_pwhash_STRBYTES, 'ascii');
    // add salt to the password
    saltedPassBuf = Buffer.concat([passwordBuf, buf], 128);
    // hash it separately multiple times
    // note, i'm not hashing the hash,
    // I'm hashing the original buffer to see what happens
    // this has no application in production
    hash = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
    hash2 = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
    hash3 = sodium.crypto_pwhash_str(saltedPassBuf, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE);
    // log it to see what I got -- not for production
    console.log(hash.toString());
    console.log(hash2.toString());
    console.log(hash3.toString());
    // save the salt and the buffer for authentication
    user.salt = buf;
    user.password = hash;
    next();
});

I get three different strings logged with that code. e.g.

$argon2i$v=19$m=32768,t=4,p=1$ayPVQ1X+xNhWmD9S5AUuaw$1mWusk59AebhzOHhl+j5JpvmRI27Pq57XG5zcAB5R4U
$argon2i$v=19$m=32768,t=4,p=1$PjTYKpfhh1bZh+MV84Y9kA$9+U33nf6efuugsrz15cEKDa5+rAHgYVA5Kqo4F1G3DE
$argon2i$v=19$m=32768,t=4,p=1$Ii8AErmAFc0na9Yi2OgCkw$ySU80Fv9OiOmeT9EV/BWon1Jjck2Lx23nOeCk0wkMPU

Now the first part of each of those is the same, making me thing the submitted password part is the same (since it is the first part of the buffer that is being hashed). So maybe it's buffers I don't understand.

But if buf remains static, why would the rest of saltedPassBuff change?

edit: had not finished writing when I accidentally submitted, edited to finish writing the question


Solution

  • In addition to your salt the pwhash function (documentations is minimal) most likely also adds its own random salt which is also included in the result for later comparison using crypto_pwhash_str_verify.

    There is also a "CPU intensive" aspect, probably an iteration. Just using a hash function with a salt does little to improve the security. A CPU intensive component needs to be added such as iteration.

    The point is to make the attacker spend a lot of time finding passwords by brute force.