master
Sven Slootweg 9 years ago
parent c87c9132da
commit 4d96794ab9

@ -7,7 +7,7 @@ This module will change and do the following things for you:
* Input values (passwords, usually) are expected in utf-8. * Input values (passwords, usually) are expected in utf-8.
* Output/hash values are base64-encoded, and can be stored directly in your data store of choice. * Output/hash values are base64-encoded, and can be stored directly in your data store of choice.
* Scrypt parameters are set to `scrypt.params(0.1)`, this can be overridden on a per-hash basis (see API documentation below). * Scrypt parameters are set to `scrypt.params(0.1)`, this can be overridden on a per-hash basis (see API documentation below).
* Scrypt errors, which are not proper Error types in the original library, are caught and rethrown as one of three correctly-inheriting Error types (see API documentation below). This means you can handle them like any other kind of Error. * Scrypt errors, which are now proper Error types in the original library but still not easily distinguishable, are caught and rethrown as one of three correctly-inheriting Error types (see API documentation below). This means you can handle them like any other kind of Error.
The API supports both Promises and nodebacks. The API supports both Promises and nodebacks.
@ -87,6 +87,12 @@ scrypt.hash("secretpassword", {}, function(err, hash){
}); });
``` ```
## Upgrading to 2.0.0
Due to changes in the underlying `scrypt` library, there has been a minor indirect change in our documented API as well. Specifically, `scrypt.scryptLib.params` is now asynchronous by default, with (poor) support for ES6 Promises. The new documentation can be found [here](https://github.com/barrysteyn/node-scrypt/blob/master/Readme.md#params). Due to its inconsistent behaviour, I recommend manual promisification using [Bluebird](https://www.npmjs.com/package/bluebird) or [`es6-promisify`](https://www.npmjs.com/package/es6-promisify).
The other changes in `scrypt` do not affect the `scrypt-for-humans` API, other than introducing support for Node.js 4. If you were not using custom `params`, you can remain using `scrypt-for-humans` like you have done previously.
## API ## API
### scrypt.hash(input, [options, [callback]]) ### scrypt.hash(input, [options, [callback]])
@ -95,7 +101,7 @@ Creates a hash.
* __input__: The input to hash, usually a password. * __input__: The input to hash, usually a password.
* __options__: *Optional.* Custom options. * __options__: *Optional.* Custom options.
* __options.params__: Sets the Scrypt parameters to use. Defaults to `scrypt.params(0.1)`. If you want to change these, you'll probably need scrypt.scryptLib (documented below). * __options.params__: Sets the Scrypt parameters to use. Defaults to `scrypt.params(0.1)`. If you want to change these, you'll probably need `scrypt.scryptLib` (documented below).
* __callback__: *Optional.* A nodeback to call upon completion. If omitted, the function will return a Promise. * __callback__: *Optional.* A nodeback to call upon completion. If omitted, the function will return a Promise.
If this is successful, the hash is returned as either the resolved Promise value or the second callback parameter, depending on the API you use. If this is successful, the hash is returned as either the resolved Promise value or the second callback parameter, depending on the API you use.

@ -2,45 +2,79 @@ scrypt = require "scrypt"
errors = require "errors" errors = require "errors"
Promise = require "bluebird" Promise = require "bluebird"
# Scrypt input/output format configuration
# FIXME: Figure out how to isolate this, so that there is a guarantee these changes won't affect any other `scrypt` imports outside of the module.
scrypt.hash.config.keyEncoding = "utf8"
scrypt.hash.config.outputEncoding = "base64"
scrypt.verify.config.keyEncoding = "utf8"
scrypt.verify.config.hashEncoding = "base64"
# Some custom error types, since the `scrypt` library doesn't have proper error handling # Some custom error types, since the `scrypt` library doesn't have proper error handling
errors.create name: "ScryptError" errors.create name: "ScryptError"
errors.create {name: "ScryptInputError", parents: errors.ScryptError} errors.create {name: "ScryptInputError", parents: errors.ScryptError}
errors.create {name: "ScryptPasswordError", parents: errors.ScryptError} errors.create {name: "ScryptPasswordError", parents: errors.ScryptError}
errors.create {name: "ScryptInternalError", parents: errors.ScryptError} errors.create {name: "ScryptInternalError", parents: errors.ScryptError}
scryptErrorMap = {
"getrlimit or sysctl(hw.usermem) failed": 1
"clock_getres or clock_gettime failed": 2
"error computing derived key": 3
"could not read salt from /dev/urandom": 4
"error in OpenSSL": 5
"malloc failed": 6
"data is not a valid scrypt-encrypted block": 7
"unrecognized scrypt format": 8
"decrypting file would take too much memory": 9
"decrypting file would take too long": 10
"password is incorrect": 11
"error writing output file": 12
"error reading input file": 13
"error unkown": -1
}
defaultParameters = Promise.promisify(scrypt.params)(0.1, undefined, undefined)
getDefaultParameters = (params) ->
# This wrapper function is to ensure that we only calculate the parameters once, but can still skip waiting for that if custom parameters were passed in anyway.
if params?
return params
else
return defaultParameters
normalizePassword = (password) ->
if Buffer.isBuffer(password)
return password
else
return new Buffer(password)
scryptHandler = (resolve, reject) -> scryptHandler = (resolve, reject) ->
# This is ridiculous, but `scrypt` doesn't have proper error-handling facilities... # Well, `scrypt` now returns real Error objects. Except now they don't have error codes anymore...
return (err, result) -> return (err, result) ->
if err? if err?
errorObj = switch err.scrypt_err_code errorObj = switch scryptErrorMap[err.message]
when 1, 2, 3, 4, 5, 6, 9, 10, 12, 13 then errors.ScryptInternalError when 1, 2, 3, 4, 5, 6, 9, 10, 12, 13, -1 then errors.ScryptInternalError
when 7, 8 then errors.ScryptInputError when 7, 8 then errors.ScryptInputError
when 11 then errors.ScryptPasswordError when 11 then errors.ScryptPasswordError
reject new errorObj(err.scrypt_err_message) reject new errorObj(err.message)
else else if result == true
resolve result resolve result
else if result == false
reject new errors.ScryptPasswordError("The password did not match.")
else
resolve result.toString("base64")
module.exports = module.exports =
hash: (password, options = {}, callback) -> hash: (password, options = {}, callback) ->
(new Promise (resolve, reject) -> # We will still manually promisify, because the behaviour of `scrypt` is not predictable. It may either synchronously throw an error or return a Promise, depending on available ECMAScript features...
options.params ?= scrypt.params(0.1) Promise.try ->
scrypt.hash password, options.params, scryptHandler(resolve, reject) getDefaultParameters(options.params)
).nodeify(callback) .then (parameters) ->
new Promise (resolve, reject) ->
scrypt.kdf normalizePassword(password), parameters, scryptHandler(resolve, reject)
.nodeify(callback)
verifyHash: (password, hash, callback) -> verifyHash: (password, hash, callback) ->
(new Promise (resolve, reject) -> (new Promise (resolve, reject) ->
scrypt.verify hash, password, scryptHandler(resolve, reject) hashBuffer = new Buffer(hash, "base64")
scrypt.verifyKdf hashBuffer, normalizePassword(password), scryptHandler(resolve, reject)
).nodeify(callback) ).nodeify(callback)
ScryptError: errors.ScryptError ScryptError: errors.ScryptError
InputError: errors.ScryptInputError InputError: errors.ScryptInputError
PasswordError: errors.ScryptPasswordError PasswordError: errors.ScryptPasswordError
InternalError: errors.ScryptInternalError InternalError: errors.ScryptInternalError
scryptLib: scrypt scryptLib: scrypt

@ -1,4 +1,4 @@
var Promise, errors, scrypt, scryptHandler; var Promise, defaultParameters, errors, getDefaultParameters, normalizePassword, scrypt, scryptErrorMap, scryptHandler;
scrypt = require("scrypt"); scrypt = require("scrypt");
@ -6,14 +6,6 @@ errors = require("errors");
Promise = require("bluebird"); Promise = require("bluebird");
scrypt.hash.config.keyEncoding = "utf8";
scrypt.hash.config.outputEncoding = "base64";
scrypt.verify.config.keyEncoding = "utf8";
scrypt.verify.config.hashEncoding = "base64";
errors.create({ errors.create({
name: "ScryptError" name: "ScryptError"
}); });
@ -33,12 +25,47 @@ errors.create({
parents: errors.ScryptError parents: errors.ScryptError
}); });
scryptErrorMap = {
"getrlimit or sysctl(hw.usermem) failed": 1,
"clock_getres or clock_gettime failed": 2,
"error computing derived key": 3,
"could not read salt from /dev/urandom": 4,
"error in OpenSSL": 5,
"malloc failed": 6,
"data is not a valid scrypt-encrypted block": 7,
"unrecognized scrypt format": 8,
"decrypting file would take too much memory": 9,
"decrypting file would take too long": 10,
"password is incorrect": 11,
"error writing output file": 12,
"error reading input file": 13,
"error unkown": -1
};
defaultParameters = Promise.promisify(scrypt.params)(0.1, void 0, void 0);
getDefaultParameters = function(params) {
if (params != null) {
return params;
} else {
return defaultParameters;
}
};
normalizePassword = function(password) {
if (Buffer.isBuffer(password)) {
return password;
} else {
return new Buffer(password);
}
};
scryptHandler = function(resolve, reject) { scryptHandler = function(resolve, reject) {
return function(err, result) { return function(err, result) {
var errorObj; var errorObj;
if (err != null) { if (err != null) {
errorObj = (function() { errorObj = (function() {
switch (err.scrypt_err_code) { switch (scryptErrorMap[err.message]) {
case 1: case 1:
case 2: case 2:
case 3: case 3:
@ -49,6 +76,7 @@ scryptHandler = function(resolve, reject) {
case 10: case 10:
case 12: case 12:
case 13: case 13:
case -1:
return errors.ScryptInternalError; return errors.ScryptInternalError;
case 7: case 7:
case 8: case 8:
@ -57,9 +85,13 @@ scryptHandler = function(resolve, reject) {
return errors.ScryptPasswordError; return errors.ScryptPasswordError;
} }
})(); })();
return reject(new errorObj(err.scrypt_err_message)); return reject(new errorObj(err.message));
} else { } else if (result === true) {
return resolve(result); return resolve(result);
} else if (result === false) {
return reject(new errors.ScryptPasswordError("The password did not match."));
} else {
return resolve(result.toString("base64"));
} }
}; };
}; };
@ -69,16 +101,19 @@ module.exports = {
if (options == null) { if (options == null) {
options = {}; options = {};
} }
return (new Promise(function(resolve, reject) { return Promise["try"](function() {
if (options.params == null) { return getDefaultParameters(options.params);
options.params = scrypt.params(0.1); }).then(function(parameters) {
} return new Promise(function(resolve, reject) {
return scrypt.hash(password, options.params, scryptHandler(resolve, reject)); return scrypt.kdf(normalizePassword(password), parameters, scryptHandler(resolve, reject));
})).nodeify(callback); });
}).nodeify(callback);
}, },
verifyHash: function(password, hash, callback) { verifyHash: function(password, hash, callback) {
return (new Promise(function(resolve, reject) { return (new Promise(function(resolve, reject) {
return scrypt.verify(hash, password, scryptHandler(resolve, reject)); var hashBuffer;
hashBuffer = new Buffer(hash, "base64");
return scrypt.verifyKdf(hashBuffer, normalizePassword(password), scryptHandler(resolve, reject));
})).nodeify(callback); })).nodeify(callback);
}, },
ScryptError: errors.ScryptError, ScryptError: errors.ScryptError,

@ -1,6 +1,6 @@
{ {
"name": "scrypt-for-humans", "name": "scrypt-for-humans",
"version": "1.0.2", "version": "2.0.0",
"description": "A human-friendly API wrapper for the Node.js Scrypt bindings.", "description": "A human-friendly API wrapper for the Node.js Scrypt bindings.",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
@ -31,6 +31,6 @@
"dependencies": { "dependencies": {
"bluebird": "^2.6.4", "bluebird": "^2.6.4",
"errors": "^0.2.0", "errors": "^0.2.0",
"scrypt": "^4.0.7" "scrypt": "^5.2.0"
} }
} }

@ -2,7 +2,6 @@ var scrypt = require("./");
var Promise = require("bluebird"); var Promise = require("bluebird");
/* Using Promises */ /* Using Promises */
var theHash; var theHash;
Promise.try(function(){ Promise.try(function(){

Loading…
Cancel
Save