3

What's the best way to implement password hashing and verification in node.js using only the built-in crypto module. Basically what is needed:

function passwordHash(password) {} // => passwordHash
function passwordVerify(password, passwordHash) {} // => boolean

People usually are using bcrypt or other third-party libs for this purpose. I wonder isn't built-in crypto module huge enough already to cover at least all basic needs?

There's scrypt(), which appears to be the right guy for this purpose, but there's no verified counterpart and nobody seems to care.

Yilmaz
  • 1
  • 7
  • 79
  • 120
disfated
  • 10,098
  • 12
  • 36
  • 48

2 Answers2

2
import { scrypt, randomBytes } from "crypto";
import { promisify } from "util";

// scrypt is callback based so with promisify we can await it
const scryptAsync = promisify(scrypt);

Hashing process has two methods. First method, you hash the password, second method, you need to compare the new sign-in password with the stored password. I use typescript to write everything in detail

export class Password {
  static async toHash(password: string) {
    const salt = randomBytes(8).toString("hex");
    const buf = (await scryptAsync(password, salt, 64)) as Buffer;
    return `${buf.toString("hex")}.${salt}`;
  }
  static async compare(storedPassword: string, suppliedPassword: string) {
   // split() returns array
    const [hashedPassword, salt] = storedPassword.split(".");
   // we hash the new sign-in password
    const buf = (await scryptAsync(suppliedPassword, salt, 64)) as Buffer;
   // compare the new supplied password with the stored hashed password
    return buf.toString("hex") === hashedPassword;
  }
}
Yilmaz
  • 1
  • 7
  • 79
  • 120
0
const password = "my_password"; 

// Creating a unique salt for a particular user
const salt = crypto.randomBytes(16).toString('hex'); 
  
// Hash the salt and password with 1000 iterations, 64 length and sha512 digest 
const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');

Store both salt and hash for the user in DB.

const re_entered_password = "my_password";

// To verify the same - salt (stored in DB) with same other parameters used while creating hash (1000 iterations, 64 length and sha512 digest)
const newHash = crypto.pbkdf2Sync(re_entered_password, salt, 1000, 64, 'sha512').toString('hex');

// check if hash (stored in DB) and newly generated hash (newHash) are the same
hash === newHash;
Prathap Reddy
  • 1,548
  • 2
  • 4
  • 18
  • Thanks for the contribution. I see two drawbacks here (not an expert, though). 1) Requirement to store salt while I believe it's already stored somehow in hash 2) Verify function shouldn't have the same computational complexity as the hashing function. – disfated Jul 15 '20 at 07:58
  • 1) IMHO we can't extract `salt` directly from `hash`. We can store both `salt` and `hash` in one field (salt+hash) like [other crypt libraries](https://stackoverflow.com/questions/5881169/what-column-type-length-should-i-use-for-storing-a-bcrypt-hashed-password-in-a-d) do. 2) I am not exactly sure on the shortcut methods to verify the same. To the best of my knowledge, we should recreate `hash` with same computational complexity to verify if it's same as the `hash` present in DB. Hope [this answer](https://stackoverflow.com/a/17201493) clear the air for you. – Prathap Reddy Jul 15 '20 at 08:41
  • Hey @disfated, did you manage to get any simple solutions? I am little curious to know. Could you please help posting the answer if any. Thanks. – Prathap Reddy Jul 30 '20 at 09:54
  • 1
    no, I used slightly modified solution from the link you gave. Simple solution is yet to come. – disfated Aug 03 '20 at 07:07
  • Sure. Not to forget to post your solution once you find the simpler one. Thanks :-) – Prathap Reddy Aug 03 '20 at 07:10