Hash your passwords with scrypt using Nodejs crypto module
Tags: node, scrypt, crypto
Published: 2020-05-28 20:50:00

From Nodejs v10, crypto module has a built-in implementation of [scrypt](https://nodejs.org/api/crypto.html#crypto_crypto_scrypt_password_salt_keylen_options_callback) algorithm that can be used as a password hashing algorithm. To the best of my knowledge, the state-of-art algorithm to hash and store passwords in Nodejs is [bcrypt](https://www.npmjs.com/package/bcrypt). bcrypt is a very popular module in NPM with nearly half a million downloads per week. I'm not a security expert to tell which one is better, but if you want to use Scrypt as another powerful hash algorithm, its simple:

## Hash Password
Scrypt is a [salted hashing algorithm](https://en.wikipedia.org/wiki/Salt_(cryptography)). In order to hash passwords using Scrypt you need to create a unique salt on every hash.  

> The salt should be as unique as possible. It is recommended that a salt is random and at least 16 bytes long. See [NIST SP 800-132](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-132.pdf) for 
```js
const crypto = require("crypto")

async function hash(password) {
    return new Promise((resolve, reject) => {
        // generate random 16 bytes long salt
        const salt = crypto.randomBytes(16).toString("hex")

        crypto.scrypt(password, salt, 64, (err, derivedKey) => {
            if (err) reject(err);
            resolve(salt + ":" + derivedKey.toString('hex'))
        });
    })
}
```
It is important to save generated salt with your hash, because without the salt there is no way to verify the password, and yes, [you can store the salt in plaintex](https://security.stackexchange.com/a/17435).

## Check Password
As I said before we need salt to verify password. The salt can be extraced from result of `hash` function.
```js
async function verify(password, hash) {
    return new Promise((resolve, reject) => {
        const [salt, key] = hash.split(":")
        crypto.scrypt(password, salt, 64, (err, derivedKey) => {
            if (err) reject(err);
            resolve(key == derivedKey.toString('hex'))
        });
    })
}
```

## Putting all together
```js
const crypto = require("crypto")

async function hash(password) {
    return new Promise((resolve, reject) => {
        const salt = crypto.randomBytes(8).toString("hex")

        crypto.scrypt(password, salt, 64, (err, derivedKey) => {
            if (err) reject(err);
            resolve(salt + ":" + derivedKey.toString('hex'))
        });
    })
}

async function verify(password, hash) {
    return new Promise((resolve, reject) => {
        const [salt, key] = hash.split(":")
        crypto.scrypt(password, salt, 64, (err, derivedKey) => {
            if (err) reject(err);
            resolve(key == derivedKey.toString('hex'))
        });
    })
}

(async function run () {
    const password1 = await hash("123456")
    const password2 = await hash("123456")
    console.log("password1", await verify("123456", password1));
    console.log("password2", await verify("123456", password2));
    console.log("password1 == password2", password1 == password2);   
})()
```
And here is the result:
```
password1 true
password2 true
password1 == password2 false
```
As you can see from the result, hashing single string with different salts results different outputs, but they both can verified.