v1.0.0
parent
f06f7a1043
commit
af0d0ae973
@ -0,0 +1,137 @@
|
||||
# scrypt-for-humans
|
||||
|
||||
A human-friendly API wrapper for the Node.js Scrypt bindings, because the default bindings kind of suck.
|
||||
|
||||
This module will change and do the following things for you:
|
||||
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
The API supports both Promises and nodebacks.
|
||||
|
||||
## License
|
||||
|
||||
[WTFPL](http://www.wtfpl.net/txt/copying/) or [CC0](https://creativecommons.org/publicdomain/zero/1.0/), whichever you prefer.
|
||||
|
||||
## Donate
|
||||
|
||||
My income consists entirely of donations for my projects. If this module is useful to you, consider [making a donation](http://cryto.net/~joepie91/donate.html)!
|
||||
|
||||
You can donate using Bitcoin, PayPal, Gratipay, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else.
|
||||
|
||||
## Contributing
|
||||
|
||||
Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the `.coffee` files, not the `.js` files.
|
||||
|
||||
As this module could potentially deal with authentication, tests are needed; a pull request for those would be especially welcome.
|
||||
|
||||
Build tool of choice is `gulp`; simply run `gulp` while developing, and it will watch for changes.
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
scrypt = require("scrypt-for-humans");
|
||||
Promise = require("bluebird");
|
||||
|
||||
/* Using Promises */
|
||||
|
||||
var theHash;
|
||||
|
||||
Promise.try(function(){
|
||||
return scrypt.hash("secretpassword");
|
||||
}).then(function(hash){
|
||||
console.log("The hash is " + hash);
|
||||
theHash = hash;
|
||||
|
||||
/* Now let's see if it verifies - number 1 is correct. */
|
||||
return scrypt.verifyHash("secretpassword", theHash);
|
||||
}).then(function(){
|
||||
console.log("Number 1 was correct!");
|
||||
}).catch(scrypt.PasswordError, function(err){
|
||||
console.log("Number 1 was wrong!");
|
||||
}).then(function(){
|
||||
/* And let's see if it fails correctly - number 2 is wrong. */
|
||||
return scrypt.verifyHash("wrongpassword", theHash);
|
||||
}).then(function(){
|
||||
console.log("Number 2 was correct!");
|
||||
}).catch(scrypt.PasswordError, function(err){
|
||||
console.log("Number 2 was wrong!");
|
||||
});
|
||||
|
||||
/* Using nodebacks */
|
||||
|
||||
scrypt.hash("secretpassword", {}, function(err, hash){
|
||||
console.log("The hash is " + hash);
|
||||
|
||||
/* Now let's see if it verifies - number 1 is correct. */
|
||||
scrypt.verifyHash("secretpassword", hash, function(err, result){
|
||||
if(err) {
|
||||
console.log("Number 1 was wrong!", err);
|
||||
} else {
|
||||
console.log("Number 1 was correct!");
|
||||
}
|
||||
|
||||
/* And let's see if it fails correctly - number 2 is wrong. */
|
||||
scrypt.verifyHash("wrongpassword", hash, function(err, result){
|
||||
if(err) {
|
||||
console.log("Number 2 was wrong!", err);
|
||||
} else {
|
||||
console.log("Number 2 was correct!");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### scrypt.hash(input, [options, [callback]])
|
||||
|
||||
Creates a hash.
|
||||
|
||||
* __input__: The input to hash, usually a password.
|
||||
* __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).
|
||||
* __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 an error occurs, either the Promise will reject with it, or it will be passed as the first callback parameter, depending on the API you use. All errors correctly inherit from `Error`, and are documented below.
|
||||
|
||||
### scrypt.verifyHash(input, hash, [callback])
|
||||
|
||||
Creates a hash.
|
||||
|
||||
* __input__: The input to hash, usually a password.
|
||||
* __hash__: The hash to verify against, in base64 encoding (the default output format of `scrypt.hash`).
|
||||
* __callback__: *Optional.* A nodeback to call upon completion. If omitted, the function will return a Promise.
|
||||
|
||||
If the input is correct and matches the hash, the Promise will resolve or the callback will be called with `true` as the value.
|
||||
|
||||
__If the input does *not* match the hash, this is considered a PasswordError, *not* a `false` value!__
|
||||
|
||||
If an error occurs, either the Promise will reject with it, or it will be passed as the first callback parameter, depending on the API you use. All errors correctly inherit from `Error`, and are documented below.
|
||||
|
||||
### scrypt.PasswordError
|
||||
|
||||
This error is thrown if the input did not match the specified hash. The original error message is retained.
|
||||
|
||||
### scrypt.InputError
|
||||
|
||||
This error is thrown if there is a different problem with the input (either the to-be-hashed value, or the hash), such as a malformed hash. The original error message is retained.
|
||||
|
||||
### scrypt.OperationalError
|
||||
|
||||
This error is thrown when an internal error of some other kind occurs in the `scrypt` library. The original error message is retained.
|
||||
|
||||
### scrypt.scryptLib
|
||||
|
||||
Provides access to the underlying `scrypt` library that is used. Useful if you want to eg. specify custom Scrypt parameters.
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.0.0
|
||||
|
||||
Initial release.
|
@ -0,0 +1 @@
|
||||
module.exports = require "./lib/scrypt-for-humans"
|
@ -0,0 +1 @@
|
||||
module.exports = require("./lib/scrypt-for-humans");
|
@ -0,0 +1,46 @@
|
||||
scrypt = require "scrypt"
|
||||
errors = require "errors"
|
||||
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
|
||||
errors.create name: "ScryptError"
|
||||
errors.create {name: "ScryptInputError", parents: errors.ScryptError}
|
||||
errors.create {name: "ScryptPasswordError", parents: errors.ScryptError}
|
||||
errors.create {name: "ScryptInternalError", parents: errors.ScryptError}
|
||||
|
||||
|
||||
scryptHandler = (resolve, reject) ->
|
||||
# This is ridiculous, but `scrypt` doesn't have proper error-handling facilities...
|
||||
return (err, result) ->
|
||||
if err?
|
||||
errorObj = switch err.scrypt_err_code
|
||||
when 1, 2, 3, 4, 5, 6, 9, 10, 12, 13 then errors.ScryptInternalError
|
||||
when 7, 8 then errors.ScryptInputError
|
||||
when 11 then errors.ScryptPasswordError
|
||||
reject new errorObj(err.scrypt_err_message)
|
||||
else
|
||||
resolve result
|
||||
|
||||
|
||||
module.exports =
|
||||
hash: (password, options = {}, callback) ->
|
||||
(new Promise (resolve, reject) ->
|
||||
options.params ?= scrypt.params(0.1)
|
||||
scrypt.hash password, options.params, scryptHandler(resolve, reject)
|
||||
).nodeify(callback)
|
||||
verifyHash: (password, hash, callback) ->
|
||||
(new Promise (resolve, reject) ->
|
||||
scrypt.verify hash, password, scryptHandler(resolve, reject)
|
||||
).nodeify(callback)
|
||||
ScryptError: errors.ScryptError
|
||||
InputError: errors.ScryptInputError
|
||||
PasswordError: errors.ScryptPasswordError
|
||||
InternalError: errors.ScryptInternalError
|
||||
scryptLib: scrypt
|
@ -0,0 +1,89 @@
|
||||
var Promise, errors, scrypt, scryptHandler;
|
||||
|
||||
scrypt = require("scrypt");
|
||||
|
||||
errors = require("errors");
|
||||
|
||||
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({
|
||||
name: "ScryptError"
|
||||
});
|
||||
|
||||
errors.create({
|
||||
name: "ScryptInputError",
|
||||
parents: errors.ScryptError
|
||||
});
|
||||
|
||||
errors.create({
|
||||
name: "ScryptPasswordError",
|
||||
parents: errors.ScryptError
|
||||
});
|
||||
|
||||
errors.create({
|
||||
name: "ScryptInternalError",
|
||||
parents: errors.ScryptError
|
||||
});
|
||||
|
||||
scryptHandler = function(resolve, reject) {
|
||||
return function(err, result) {
|
||||
var errorObj;
|
||||
if (err != null) {
|
||||
errorObj = (function() {
|
||||
switch (err.scrypt_err_code) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 9:
|
||||
case 10:
|
||||
case 12:
|
||||
case 13:
|
||||
return errors.ScryptInternalError;
|
||||
case 7:
|
||||
case 8:
|
||||
return errors.ScryptInputError;
|
||||
case 11:
|
||||
return errors.ScryptPasswordError;
|
||||
}
|
||||
})();
|
||||
return reject(new errorObj(err.scrypt_err_message));
|
||||
} else {
|
||||
return resolve(result);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
hash: function(password, options, callback) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
return (new Promise(function(resolve, reject) {
|
||||
if (options.params == null) {
|
||||
options.params = scrypt.params(0.1);
|
||||
}
|
||||
return scrypt.hash(password, options.params, scryptHandler(resolve, reject));
|
||||
})).nodeify(callback);
|
||||
},
|
||||
verifyHash: function(password, hash, callback) {
|
||||
return (new Promise(function(resolve, reject) {
|
||||
return scrypt.verify(hash, password, scryptHandler(resolve, reject));
|
||||
})).nodeify(callback);
|
||||
},
|
||||
ScryptError: errors.ScryptError,
|
||||
InputError: errors.ScryptInputError,
|
||||
PasswordError: errors.ScryptPasswordError,
|
||||
InternalError: errors.ScryptInternalError,
|
||||
scryptLib: scrypt
|
||||
};
|
@ -0,0 +1,51 @@
|
||||
scrypt = require("./");
|
||||
Promise = require("bluebird");
|
||||
|
||||
/* Using Promises */
|
||||
|
||||
var theHash;
|
||||
|
||||
Promise.try(function(){
|
||||
return scrypt.hash("secretpassword");
|
||||
}).then(function(hash){
|
||||
console.log("The hash is " + hash);
|
||||
theHash = hash;
|
||||
|
||||
/* Now let's see if it verifies - number 1 is correct. */
|
||||
return scrypt.verifyHash("secretpassword", theHash);
|
||||
}).then(function(){
|
||||
console.log("Number 1 was correct!");
|
||||
}).catch(scrypt.PasswordError, function(err){
|
||||
console.log("Number 1 was wrong!");
|
||||
}).then(function(){
|
||||
/* And let's see if it fails correctly - number 2 is wrong. */
|
||||
return scrypt.verifyHash("wrongpassword", theHash);
|
||||
}).then(function(){
|
||||
console.log("Number 2 was correct!");
|
||||
}).catch(scrypt.PasswordError, function(err){
|
||||
console.log("Number 2 was wrong!");
|
||||
});
|
||||
|
||||
/* Using nodebacks */
|
||||
|
||||
scrypt.hash("secretpassword", {}, function(err, hash){
|
||||
console.log("The hash is " + hash);
|
||||
|
||||
/* Now let's see if it verifies - number 1 is correct. */
|
||||
scrypt.verifyHash("secretpassword", hash, function(err, result){
|
||||
if(err) {
|
||||
console.log("Number 1 was wrong!", err);
|
||||
} else {
|
||||
console.log("Number 1 was correct!");
|
||||
}
|
||||
|
||||
/* And let's see if it fails correctly - number 2 is wrong. */
|
||||
scrypt.verifyHash("wrongpassword", hash, function(err, result){
|
||||
if(err) {
|
||||
console.log("Number 2 was wrong!", err);
|
||||
} else {
|
||||
console.log("Number 2 was correct!");
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue