Awesome
argon2-ffi
Node.js bindings for argon2
, the winner of the Password
Hashing Competition (PHC), and the current recommendation for
password storage by the Open Web Application Security Project
(OWASP).
argon2-ffi
supports NodeJS LTS releases and the current NodeJS release.
Calling CPU-intensive tasks like password hashing and validation are performed
asynchronously by dispatching the work to a separate thread pool using
node-ffi
, which in turn uses libuv
, so your main
application can continue to do other work while these tasks are executed. All
asynchronous operations return Promises, with a type defined by
any-promise.
Installation
npm install --save argon2-ffi
Usage
This module exports argon2i
and argon2d
. These are two variants
of argon2
with different use-cases and tradeoffs. To find which
one you should use, refer to the argon2
repo.
Hashing a password
const { argon2i } = require("argon2-ffi");
// const argon2d = require('argon2-ffi').argon2d; if you'd like to use argon2d
const crypto = require("crypto");
const util = require("util");
const getRandomBytes = util.promisify(crypto.randomBytes);
async function main() {
const password = "password1"; // Can also be a Buffer
const salt = await getRandomBytes(32);
const hashedPassword = await argon2i.hash(password, salt);
console.log(hashedPassword);
}
main();
In this example, crypto.randomBytes is used to generate a salt. This is the best practice as the salt is guaranteed to be cryptographically secure. However, you can of course use your own buffer.
.hash
takes a few options, too! You can specify timeCost
(default 3
),
memoryCost
(default 4096
), parallelism
(default
1
), and hashLength
(default 32
). Changing any of these parameters will
have an effect on the output hash.
const { argon2i } = require("argon2-ffi");
const crypto = require("crypto");
const util = require("util");
const getRandomBytes = util.promisify(crypto.randomBytes);
async function main() {
const password = Buffer.from("password1");
const options = {
timeCost: 4,
memoryCost: 16384,
parallelism: 2,
hashLength: 64,
};
const salt = await getRandomBytes(32);
const hashedPassword = await argon2i.hash(password, salt, options);
console.log(hashedPassword);
}
main();
The result of running .hash
is a string that encodes all of the options used
to produce the hash, so to verify passwords later, this string is all you need,
as we'll see in the next section.
Verifying a password
const { argon2i } = require("argon2-ffi");
async function main() {
const encodedHash =
"$argon2i$v=19$m=4096,t=3,p=1$c2FsdHlzYWx0$oG0js25z7kM30xSg9+nAKtU0hrPa0UnvRnqQRZXHCV8";
const password = Buffer.from("password1");
const isCorrect = await argon2i.verify(encodedHash, password);
console.log(isCorrect ? "Correct password!" : "Incorrect password");
}
main();
Differences from node-argon2
argon2-ffi
was originally written to address an issue
with running node-argon2
in a web server. This was a
non-starter for my own projects. By using node-ffi
, argon2-ffi
was able to
circumvent the problems node-argon2
had with Promises. node-argon2
has
since resolved this issue. argon2-ffi
also returned Promises with
any-promise
, but this has since been implemented in node-argon2
as well.
Today, the practical differences between the two libraries are only in the
public APIs.
Contributing
To build:
git submodule init
git submodule update
node-gyp rebuild