Node.js bindings for argon2 password hashing and proof-of-work algorithm
npm install argon2-ffiNode.js bindings for [argon2][argon2], the winner of the Password
Hashing Competition (PHC), and the current recommendation for
password storage by the [Open Web Application Security Project
(OWASP)][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][async-library-calls], 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.
[async-library-calls]: https://github.com/node-ffi/node-ffi/wiki/Node-FFI-Tutorial#async-library-calls
npm install --save argon2-ffi
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][argon2].
``javascript
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 (default1), and hashLength (default 32). Changing any of these parameters will
have an effect on the output hash.
`javascript
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.
`javascript
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();
`
argon2-ffi was originally written to address [an issue][node-argon2-issue]node-argon2
with running [][node-argon2] in a web server. This was anode-ffi
non-starter for my own projects. By using , argon2-ffi was able tonode-argon2
circumvent the problems had with Promises. node-argon2 hasargon2-ffi
since resolved this issue. also returned Promises withany-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.
[node-argon2-issue]: https://github.com/ranisalt/node-argon2/issues/33
[node-argon2]: https://github.com/ranisalt/node-argon2
To build:
git submodule init
git submodule update
node-gyp rebuild
[argon2]: https://github.com/P-H-C/phc-winner-argon2
[owasp]: https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet