Comparing version 4.0.7 to 5.0.0
419
index.js
@@ -1,117 +0,372 @@ | ||
var scrypt = require('./build/Release/scrypt'); | ||
"use strict"; | ||
var scryptNative = require("./build/Release/scrypt"); | ||
var checkNumberOfArguments = function(args, message, numberOfArguments) { | ||
if (message === undefined) message = "No arguments present"; | ||
if (numberOfArguments === undefined) numberOfArguments = 1; | ||
if (args.length < numberOfArguments) { | ||
var error = new SyntaxError(message); | ||
throw error; | ||
} | ||
} | ||
// | ||
//Create function instances | ||
// Checks async arguments. Will throw error if callback does not exist and | ||
// promises are not available | ||
// | ||
scrypt.passwordHash = scrypt.Hash(); | ||
scrypt.verifyHash = scrypt.Verify(); | ||
scrypt.hash = scrypt.Hash(); | ||
scrypt.verify = scrypt.Verify(); | ||
scrypt.params = scrypt.Params(); | ||
scrypt.kdf = scrypt.KDF(); | ||
var checkAsyncArguments = function(args, callback_least_needed_pos, message) { | ||
checkNumberOfArguments(args); | ||
var callback_index = (function(){ | ||
for (var i=0; i < args.length; i++) { | ||
if (typeof args[i] === "function") { | ||
return i; | ||
} | ||
} | ||
})(); | ||
if (callback_index === undefined) { | ||
if (typeof Promise !== "undefined") | ||
return undefined; // if promises are available, don't worry about call backs | ||
var error = new SyntaxError("No callback function present, and Promises are not available"); | ||
throw error; | ||
} | ||
if (callback_index < callback_least_needed_pos) { | ||
var error = new SyntaxError(message); | ||
throw error; | ||
} | ||
return callback_index; | ||
} | ||
// | ||
//Parses input arguments for scrypt parameter object or translation function inputs | ||
// Checks the scrypt parameters object | ||
// | ||
function parseScryptParameters(args, startIndex) { | ||
var i = 0, | ||
paramsObject = {}; | ||
var checkScryptParametersObject = function(params) { | ||
var error = undefined; | ||
for (i=startIndex; i < startIndex+3 && typeof args[i] != "undefined"; i++) { | ||
if (i - startIndex > 0 && (typeof args[i] === "function" || typeof args[i] === "boolean")) { | ||
break; | ||
if (typeof params !== "object") { | ||
var error = new TypeError("Scrypt parameters type is incorrect: It must be a JSON object"); | ||
} | ||
if (!error && !params.hasOwnProperty("N")) { | ||
var error = new TypeError("Scrypt params object does not have 'N' property present"); | ||
} | ||
if (!error && params.N !== parseInt(params.N)) { | ||
var error = new TypeError("Scrypt params object 'N' property is not an integer"); | ||
} | ||
if (!error && !params.hasOwnProperty("r")) { | ||
var error = new TypeError("Scrypt params object does not have 'r' property present"); | ||
} | ||
if (!error && params.r !== parseInt(params.r)) { | ||
var error = new TypeError("Scrypt params object 'r' property is not an integer"); | ||
} | ||
if (!error && !params.hasOwnProperty("p")) { | ||
var error = new TypeError("Scrypt params object does not have 'p' property present"); | ||
} | ||
if (!error && params.p !== parseInt(params.p)) { | ||
var error = new TypeError("Scrypt params object 'p' property is not an integer"); | ||
} | ||
if (error) { | ||
error.propertyName = "Scrypt parameters object"; | ||
error.propertyValue = params; | ||
throw error; | ||
} | ||
} | ||
var processParamsArguments = function(args) { | ||
var error = undefined; | ||
checkNumberOfArguments(args, "At least one argument is needed - the maxtime", 1); | ||
// Set defaults (if necessary) | ||
if (args[1] === undefined) args[1] = 0; //maxmem default to 0 | ||
if (args[2] === undefined) args[2] = 0.5; //max_memfrac default to 0.5 | ||
for(var i=0; i < Math.min(3, args.length); i++) { | ||
var propertyName = (function() { | ||
if (i === 0) return "maxtime"; | ||
if (i === 1) return "maxmem"; | ||
if (i === 2) return "max_memfrac"; | ||
})(); | ||
// All args must be of type number | ||
if (!error && typeof args[i] !== "number") { | ||
error = new TypeError(propertyName + " must be a number"); | ||
} | ||
switch(typeof args[i]) { | ||
case "number": | ||
if (i - startIndex == 0) { | ||
paramsObject.maxtime = args[i]; | ||
} | ||
if (i - startIndex == 1) { | ||
paramsObject.maxmem = args[i]; | ||
} | ||
if (i - startIndex == 2) { | ||
paramsObject.maxmemfrac = args[i]; | ||
} | ||
// Specific argument checks | ||
if (!error) { | ||
switch (i) { | ||
case 0: //maxtime | ||
if (args[0] <= 0) { | ||
error = new RangeError(propertyName + " must be greater than 0"); | ||
} | ||
break; | ||
break; | ||
case 1: //maxmem | ||
if (args[1] !== parseInt(args[1], 10)) { | ||
error = new TypeError(propertyName + " must be an integer"); | ||
} | ||
default: | ||
if (i-startIndex == 0) { | ||
throw scrypt.errorObject(1, "expecting maxtime as a number"); | ||
} | ||
if (!error && args[1] < 0) { | ||
error = new RangeError(propertyName + " must be greater than or equal to 0") | ||
} | ||
break; | ||
if (i-startIndex == 1) { | ||
throw scrypt.errorObject(1, "expecting maxmem as a number"); | ||
} | ||
if (i-startIndex == 2) { | ||
throw scrypt.errorObject(1, "expecting maxmemfrac as a number"); | ||
} | ||
case 2: //max_memfrac | ||
if (args[2] < 0.0 || args[2] > 1.0) { | ||
error = new RangeError(propertyName + " must be between 0.0 and 1.0 inclusive") | ||
} | ||
break; | ||
} | ||
} | ||
break; | ||
// Throw error if necessary | ||
if (error) { | ||
error.propertyName = propertyName; | ||
error.propertyValue = args[i]; | ||
throw error; | ||
} | ||
} | ||
return paramsObject; | ||
return args; | ||
} | ||
var processKDFArguments = function(args) { | ||
checkNumberOfArguments(args, "At least two arguments are needed - the key and the Scrypt paramaters object", 2) | ||
// | ||
// Check key argument | ||
// | ||
if (typeof args[0] === "string") | ||
// Convert string to buffer (if necessary) | ||
args[0] = new Buffer(args[0]); | ||
else if (!Buffer.isBuffer(args[0])) { | ||
var error = new TypeError("Key type is incorrect: It can only be of type string or Buffer"); | ||
error.propertyName = "key"; | ||
error.propertyValue = args[0]; | ||
throw error; | ||
} | ||
// | ||
// Check Scrypt Parameters object | ||
// | ||
checkScryptParametersObject(args[1]) | ||
return args; | ||
} | ||
var processVerifyArguments = function(args) { | ||
checkNumberOfArguments(args, "At least two arguments are needed - the KDF and the key", 2); | ||
// | ||
// Check KDF | ||
// | ||
if (typeof args[0] === "string") | ||
// Convert string to buffer (if necessary) | ||
args[0] = new Buffer(args[0]); | ||
else if (!Buffer.isBuffer(args[0])) { | ||
var error = new TypeError("KDF type is incorrect: It can only be of type string or Buffer"); | ||
error.propertyName = "KDF"; | ||
error.propertyValue = args[0]; | ||
throw error; | ||
} | ||
// | ||
// Check Key | ||
// | ||
if (typeof args[1] === "string") | ||
// Convert string to buffer (if necessary) | ||
args[1] = new Buffer(args[1]); | ||
else if (!Buffer.isBuffer(args[1])) { | ||
var error = new TypeError("Key type is incorrect: It can only be of type string or Buffer"); | ||
error.propertyName = "key"; | ||
error.propertyValue = args[1]; | ||
throw error; | ||
} | ||
return args; | ||
} | ||
var processHashArguments = function(args) { | ||
checkNumberOfArguments(args, "At least four arguments are needed - the key to hash, the scrypt params object, the output length of the hash and the salt", 4); | ||
// | ||
// Check Key | ||
// | ||
if (typeof args[0] === "string") | ||
// Convert string to buffer (if necessary) | ||
args[0] = new Buffer(args[0]); | ||
else if (!Buffer.isBuffer(args[0])) { | ||
var error = new TypeError("Key type is incorrect: It can only be of type string or Buffer"); | ||
error.propertyName = "KDF"; | ||
error.propertyValue = args[0]; | ||
throw error; | ||
} | ||
// | ||
// Check Scrypt Parameters object | ||
// | ||
checkScryptParametersObject(args[1]) | ||
// | ||
// Check the hash output length | ||
// | ||
if (typeof args[2] !== "number" || args[2] !== parseInt(args[2],10)) { | ||
error = new TypeError("Hash length must be an integer"); | ||
throw error; | ||
} | ||
// | ||
// Check Salt | ||
// | ||
if (typeof args[3] === "string") | ||
// Convert string to buffer (if necessary) | ||
args[3] = new Buffer(args[3]); | ||
else if (!Buffer.isBuffer(args[3])) { | ||
var error = new TypeError("Salt type is incorrect: It can only be of type string or Buffer"); | ||
error.propertyName = "salt"; | ||
error.propertyValue = args[3]; | ||
throw error; | ||
} | ||
return args; | ||
} | ||
// | ||
// Scrypt Password Hash | ||
// Scrypt Object | ||
// | ||
scrypt.passwordHash = function(passwordHash, params) { | ||
var scrypt = { | ||
paramsSync: function() { | ||
var args = processParamsArguments(arguments); | ||
return scryptNative.paramsSync(args[0], args[1], args[2]); | ||
}, | ||
var retFunction = function() { | ||
var args = Array.prototype.slice.apply(arguments), | ||
paramsObject; | ||
params: function() { | ||
var args = arguments | ||
, callback_index = checkAsyncArguments(args, 1, "At least one argument is needed before the callback - the maxtime"); | ||
//Determine if there are too little arguments | ||
if (args.length < 2) { | ||
throw scrypt.errorObject(1, "wrong number of arguments - at least two arguments are needed - key and scrypt parameters JSON object"); | ||
if (callback_index === undefined) { | ||
// Promise | ||
return new Promise(function(resolve, reject) { | ||
args = processParamsArguments(args); | ||
scryptNative.params(args[0], args[1], args[2], function(err, params) { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(params); | ||
} | ||
}); | ||
}) | ||
} else { | ||
// Normal async with callback | ||
// If not using promise (so using callback), | ||
// remove callback function from args and | ||
// put it in it's own variable. This allows | ||
// sync check to be used (DRY) | ||
var callback = args[callback_index]; | ||
delete args[callback_index]; | ||
args = processParamsArguments(args); | ||
args[3] = callback; | ||
scryptNative.params(args[0], args[1], args[2], args[3]); | ||
} | ||
}, | ||
//Determine if translation function is needed | ||
if (args.length > 1 && typeof args[1] !== "object" && typeof args[1] !== "function") { | ||
paramsObject = parseScryptParameters(arguments, 1); | ||
kdfSync: function() { | ||
var args = processKDFArguments(arguments); | ||
return scryptNative.kdfSync(args[0], args[1]); | ||
}, | ||
kdf: function() { | ||
var args = arguments | ||
, callback_index = checkAsyncArguments(args, 2, "At least two arguments are needed before the call back function - the key and the Scrypt parameters object"); | ||
if (callback_index === undefined) { | ||
// Promise | ||
return new Promise(function(resolve, reject) { | ||
args = processKDFArguments(args); | ||
scryptNative.kdf(args[0], args[1], function(err, kdfResult) { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(kdfResult); | ||
} | ||
}) | ||
}); | ||
} else { | ||
// Normal async with callback | ||
args = processKDFArguments(arguments); | ||
scryptNative.kdf(args[0], args[1], args[2]); | ||
} | ||
}, | ||
//Asyc | ||
if (typeof args[args.length-1] === "function") { | ||
if (typeof paramsObject !== "undefined") { | ||
params(paramsObject.maxtime, paramsObject.maxmem, paramsObject.maxmemfrac, function(err, scryptParams) { | ||
args.splice(1,Object.keys(paramsObject).length,scryptParams); | ||
passwordHash(args[0], args[1], args[2]); | ||
verifyKdfSync: function() { | ||
var args = processVerifyArguments(arguments); | ||
return scryptNative.verifySync(args[0], args[1]); | ||
}, | ||
verifyKdf: function() { | ||
var args = arguments | ||
, callback_index = checkAsyncArguments(args, 2, "At least two arguments are needed before the callback function - the KDF and the key"); | ||
if (callback_index === undefined) { | ||
// Promise | ||
return new Promise(function(resolve, reject) { | ||
args = processVerifyArguments(args); | ||
scryptNative.verify(args[0], args[1], function(err, match) { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(match); | ||
} | ||
}); | ||
} else { | ||
passwordHash(args[0], args[1], args[2]); | ||
} | ||
//Sync | ||
}) | ||
} else { | ||
if (typeof paramsObject !== "undefined") { | ||
var scryptParams = params(paramsObject.maxtime, paramsObject.maxmem, paramsObject.maxmemfrac); | ||
args.splice(1, Object.keys(paramsObject).length, scryptParams); | ||
} | ||
return passwordHash(args[0], args[1]); | ||
// Normal async with callback | ||
args = processVerifyArguments(args); | ||
scryptNative.verify(args[0], args[1], args[2]); | ||
} | ||
} | ||
retFunction.config = passwordHash.config; | ||
}, | ||
return retFunction; | ||
}(scrypt.passwordHash, scrypt.params); | ||
hashSync: function() { | ||
var args = processHashArguments(arguments); | ||
return scryptNative.hashSync(args[0], args[1], args[2], args[3]); | ||
}, | ||
// | ||
// Backward Compatbility | ||
// | ||
scrypt.passwordHash.config.keyEncoding = "ascii"; | ||
scrypt.passwordHash.config.outputEncoding = "base64"; | ||
scrypt.verifyHash.config.hashEncoding = "base64"; | ||
scrypt.verifyHash.config.keyEncoding = "ascii"; | ||
hash: function() { | ||
var args = arguments | ||
, callback_index = checkAsyncArguments(args, 4, "At least four arguments are needed before the callback - the key to hash, the scrypt params object, the output length of the hash and the salt"); | ||
scrypt.passwordHashSync = scrypt.passwordHash; | ||
scrypt.verifyHashSync = scrypt.verifyHash; | ||
if (callback_index === undefined) { | ||
//Promise | ||
return new Promise(function(resolve, reject) { | ||
args = processHashArguments(args); | ||
scryptNative.hash(args[0], args[1], args[2], args[3], function(err, hash) { | ||
if (err) { | ||
reject(err); | ||
} else { | ||
resolve(hash); | ||
} | ||
}); | ||
}); | ||
} else { | ||
// Normal async with callback | ||
args = processHashArguments(arguments); | ||
scryptNative.hash(args[0], args[1], args[2], args[3], args[4]); | ||
} | ||
} | ||
}; | ||
module.exports = scrypt; |
{ | ||
"name": "scrypt", | ||
"description": "The scrypt crypto library for NodeJS", | ||
"version": "4.0.7", | ||
"version": "5.0.0", | ||
"license": "zlib", | ||
"keywords": [ | ||
@@ -31,6 +32,8 @@ "scrypt", | ||
"devDependencies": { | ||
"tap": "*" | ||
"mocha": "2.2.5", | ||
"chai": "3.0.0", | ||
"chai-as-promised": "^5.1.0" | ||
}, | ||
"dependencies": { | ||
"nan": "^1.6.2" | ||
"nan": "^2.0.8" | ||
}, | ||
@@ -46,4 +49,4 @@ "licenses": [ | ||
"scripts": { | ||
"test": "npm install && node tests/scrypt-tests.js" | ||
"test": "mocha tests/scrypt-tests.js" | ||
} | ||
} |
716
Readme.md
@@ -1,34 +0,35 @@ | ||
# Scrypt For Node/IO | ||
# Scrypt For Node | ||
[![Build Status](https://travis-ci.org/barrysteyn/node-scrypt.png?branch=master)](https://travis-ci.org/barrysteyn/node-scrypt) [![npm version](https://badge.fury.io/js/scrypt.svg)](http://badge.fury.io/js/scrypt) | ||
[![Build Status](https://travis-ci.org/barrysteyn/node-scrypt.png?branch=master)](https://travis-ci.org/barrysteyn/node-scrypt) | ||
[![npm version](https://badge.fury.io/js/scrypt.svg)](http://badge.fury.io/js/scrypt) | ||
Scrypt for Node/IO is a native node/io C++ wrapper for Colin Percival's scrypt utility. | ||
Scrypt for Node/IO is a native node/io C++ wrapper for Colin Percival's [scrypt] | ||
(http://www.tarsnap.com/scrypt.html) cryptographic hash utility. | ||
As should be the case with any security tool, this library should be scrutinized by anyone using it. If you find or suspect an issue with the code- please bring it to my attention and I'll spend some time trying to make sure that this tool is as secure as possible. | ||
As should be the case with any security tool, this library should be scrutinized | ||
by anyone using it. If you find or suspect an issue with the code- please bring | ||
it to my attention and I'll spend some time trying to make sure that this tool is | ||
as secure as possible. | ||
# News And Updates | ||
## Node-Scrypt Version 5 | ||
Version 5 is a major new release that is **not backward compatible** with any | ||
previous version. Some highlights: | ||
## Node-Scrypt Version 4 | ||
Fully compatible with Node versions 0.10x and up and IO. Library rewritten using [nan](https://github.com/rvagg/nan). | ||
* C++ addon code rewritten: | ||
* Using [Nan 2.x](https://github.com/nodejs/nan) | ||
* Code has been greatly simplified | ||
* ES6 Promise aware. | ||
* API has changed: | ||
* Every output is a buffer. | ||
* Separated functions into async and sync versions. | ||
* Api name swap: What was kdf in previous versions is now hash (and vice versa). | ||
* Async functions will return a Promise if no callback function is present and Promises are available (else it will throw a SyntaxError). | ||
* Using correct [JavaScript Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object for all errors. | ||
## Node-Scrypt Version 3 | ||
Version 3's main highlight is support for the **Microsoft Windows** platform. | ||
### Migrating To Version 5 | ||
Version 5 is not backward compatible, but it should still be easy to migrate. | ||
Please read the [api section]() to see what's changed. One big change that is | ||
worth noting is a name change: What used to be called **hash** has now been | ||
changed to **kdf** and conversely, what was **kdf** is now called **hash**. | ||
### Node-Scrypt Version 2 | ||
Node-Scrypt version 2.0 is a complete rewrite of the previous module. It's main highlights are: | ||
* Access to the underlying key derivation function | ||
* Extensive use of node's buffers | ||
* Easy configuration | ||
* Removal of scrypt encryption/decryption (this will soon be moved to another module) | ||
The module consists of four functions: | ||
1. [params](#params) - a translation function that produces scrypt parameters | ||
2. [hash](#hash) - produces a 256 bit hash using scrypt's key derivation function | ||
3. [verify](#verify) - verify's a hash produced by this module | ||
4. [kdf](#key-derivation-function) - scrypt's underlying key dervivation function | ||
It also consists of four extra functions that provide [backward compatibility](#backward-compatibility-for-users-of-version-1x) to the previous version. | ||
## Table Of Contents | ||
@@ -38,3 +39,7 @@ | ||
* [Installation Instructions](#installation-instructions) | ||
* [API](#api) | ||
* [API](#api) - The module consists of four functions: | ||
* [params](#params) - a translation function that produces scrypt parameters | ||
* [kdf](#kdf) - a key derivation function designed for password hashing | ||
* [verifyKdf](#verifykdf) - checks if a key matches a kdf | ||
* [hash](#hash) - the raw underlying scrypt hash function | ||
* [Example Usage](#example-usage) | ||
@@ -44,35 +49,33 @@ * [FAQ](#faq) | ||
## Scrypt | ||
Scrypt is an advanced crypto library used mainly for [key derivation](http://en.wikipedia.org/wiki/Key_derivation_function): More information can be found here: | ||
# Scrypt | ||
Scrypt is an advanced crypto library used mainly for [key derivation](http://en.wikipedia.org/wiki/Key_derivation_function): | ||
More information can be found here: | ||
* [Tarsnap blurb about scrypt](http://www.tarsnap.com/scrypt.html) - Colin Percival (the author of scrypt) explains a bit about it. | ||
* [Tarsnap blurb about scrypt](http://www.tarsnap.com/scrypt.html) - Colin Percival | ||
(the author of scrypt) explains a bit about it. | ||
* [Academic paper explaining scrypt](http://www.tarsnap.com/scrypt/scrypt.pdf). | ||
* [Wikipedia Article on scrypt](http://en.wikipedia.org/wiki/Scrypt). | ||
## Installation Instructions | ||
# Installation Instructions | ||
## Pre-Requisistes | ||
### Windows | ||
#### Node-Gyp | ||
* [Node-Gyp](https://github.com/TooTallNate/node-gyp) for Windows: | ||
* Installation instructions: [node-gyp for windows](https://github.com/TooTallNate/node-gyp#installation) | ||
* Look [here](https://github.com/TooTallNate/node-gyp/wiki/Visual-Studio-2010-Setup) for additional information/helpful hints. | ||
* OppenSSL for Windows: | ||
* [OpenSSL For Windows 32 bit](https://slproweb.com/download/Win32OpenSSL-1_0_2d.exe) | ||
* [OpenSSL For Windows 64 bit](https://slproweb.com/download/Win64OpenSSL-1_0_2d.exe) | ||
To install node-gyp for windows, refer to the [windows specific install instructions](https://github.com/TooTallNate/node-gyp#installation) of the node-gyp documentation (also look [here](https://github.com/TooTallNate/node-gyp/wiki/Visual-Studio-2010-Setup) for helpful hints). | ||
#### OpenSSL | ||
It is very important that OpenSSL for windows be installed: | ||
* [OpenSSL For Windows 32 bit](http://slproweb.com/download/Win32OpenSSL-1_0_2.exe) | ||
* [OpenSSL For Windows 64 bit](http://slproweb.com/download/Win64OpenSSL-1_0_2.exe) | ||
### Posix Environment Prerequisites (Linux, Mac etc) | ||
#### Node-Gyp | ||
### Linux/MacOS | ||
[Node-gyp](https://github.com/TooTallNate/node-gyp) is needed to build this module. It should be installed globally, that is, with the `-g` switch: | ||
npm install -g node-gyp | ||
npm install -g node-gyp | ||
### From NPM | ||
## Install From NPM | ||
npm install scrypt | ||
### From Source | ||
## Install From Source | ||
@@ -84,3 +87,3 @@ git clone https://github.com/barrysteyn/node-scrypt.git | ||
### Testing | ||
## Testing | ||
To test, go to the folder where scrypt was installed, and type: | ||
@@ -90,432 +93,231 @@ | ||
#### Encodings | ||
The following encodings are accepted: | ||
# API | ||
1. **ascii** | ||
2. **utf8** | ||
3. **base64** | ||
4. **ucs2** | ||
5. **binary** | ||
6. **hex** | ||
7. **buffer** | ||
## params | ||
Translates human understandable parameters to scrypt's internal parameters. | ||
The last encoding is node's [Buffer](http://nodejs.org/api/buffer.html) object. Buffer is useful for representing raw binary data and has the ability to translate into any of the encodings mentioned above. It is for these reasons that encodings default to buffer in this module. | ||
> | ||
scrypt.paramsSync <br> | ||
scrypt.params(maxtime, [maxmem, [max_memfrac]], [function(err, obj) {}]) | ||
### Params | ||
The [params function](#params-1) translates human understandable parameters to scrypt's internal parameters. | ||
* maxtime - [REQUIRED] - a decimal (double) representing the maximum amount of time in seconds scrypt will spend when computing the derived key. | ||
* maxmem - [OPTIONAL] - an integer, specifying the maximum number of bytes of RAM used when computing the derived encryption key. If not present, will default to 0. | ||
* maxmemfrac - [OPTIONAL only if maxmem is present] - a double value between 0.0 and 1.0, representing the fraction (normalized percentage value) of the available RAM used when computing the derived key. If not present, will default to 0.5. | ||
* callback_function - [OPTIONAL] - not applicable to synchronous function. If present in async function, then it will be treated as a normal async callback. If not present, a Promise will be returned if ES6 promises are available. If not present and ES6 promises are not present, a SyntaxError will be thrown. | ||
The human understandable parameters are as follows: | ||
## kdf | ||
**Note**: In previous versions, this was called *hash*. | ||
1. **maxtime**: the maximum amount of time scrypt will spend when computing the derived key. | ||
2. **maxmemfrac**: the maximum fraction of the available RAM used when computing the derived key. | ||
3. **maxmem**: the maximum number of bytes of RAM used when computing the derived encryption key. | ||
Produces a key derivation function that uses the scrypt hash function. This | ||
should be used for hashing and checking passwords (see (using scrypt passwords)[#using-scrypt-with-passwords] for reasons why). | ||
It was designed by Colin Percival, the author of scrypt. The format | ||
can be seen [here](http://security.stackexchange.com/questions/88678/why-does-node-js-scrypt-function-use-hmac-this-way/91050#91050). | ||
Scrypt's internal parameters are as follows: | ||
> | ||
scrypt.kdfSync <br> | ||
scrypt.kdf(key, paramsObject, [function(err, obj){}]) | ||
1. **N** - general work factor, iteration count. | ||
2. **r** - blocksize in use for underlying hash; fine-tunes the relative memory-cost. | ||
3. **p** - parallelization factor; fine-tunes the relative cpu-cost. | ||
* key - [REQUIRED] - a string (or buffer) representing the key (password) that is to be hashed. | ||
* paramsObject - [REQUIRED] - parameters to control scrypt hashing (see params above). | ||
* callback_function - [OPTIONAL] - not applicable to synchronous function. If present in async function, then it will be treated as a normal async callback. If not present, a Promise will be returned if ES6 promises are available. If not present and ES6 promises are not present, a SyntaxError will be thrown. | ||
For info on what the above parameters do, read [section 5 of the scrypt ietf draft](http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-01#section-5). | ||
## verifyKdf | ||
#### How Memory Is Calculated | ||
`maxmem` is often defaulted to `0`. This does not mean that `0` RAM is used. Instead, memory used is calculated like so (quote from Colin Percival): | ||
Checks if a key (password) matches a kdf. | ||
> the system [will use] the amount of RAM which [is] specified [as the] fraction of the available RAM, but no more than maxmem, and no less than 1MiB | ||
> | ||
scrypt.verifyKdfSync <br> | ||
scrypt.verifyKdf(kdf, key, [function(err, result){}]) | ||
Therefore at the very least, 1MiB of ram will be used. | ||
* kdf [REQUIRED] - see kdf above. | ||
* key - [REQUIRED] - a string (or buffer) representing the key (password) that is to be checked. | ||
* callback_function - [OPTIONAL] - not applicable to synchronous function. If present in async function, then it will be treated as a normal async callback. If not present, a Promise will be returned if ES6 promises are available. If not present and ES6 promises are not present, a SyntaxError will be thrown. | ||
### Hash | ||
The [hash function](#hash-1) does the following: | ||
## hash | ||
**Note**: In previous versions, this was called *kdf*. | ||
* Adds random salt. | ||
* Creates a HMAC to protect against active attack. | ||
* Uses the scrypt key derivation function to derive a hash for a key. | ||
This is the raw scrypt hash function. | ||
#### Hash Format | ||
All hashes start with the word *"scrypt"*. Next comes the scrypt parameters used in the key derivation function, followed by random salt. Finally, a 256 bit HMAC of previous content is appended, with the key for the HMAC being produced by the scrypt key derivation function. The result is a 768 bit (96 byte) output: | ||
> | ||
scrypt.hashSync <br> | ||
scrypt.hash(key, paramsObject, output_length, function(err, obj){}) | ||
1. **bytes 0-5**: The word *"scrypt"* | ||
2. **bytes 6-15**: Scrypt parameters N, r, and p | ||
3. **bytes 16-47**: 32 bits of random salt | ||
4. **bytes 48-63**: A 16 bit checksum | ||
5. **bytes 64-95**: A 32 bit HMAC of bytes 0 to 63 using a key produced by the scrypt key derivation function. | ||
* key - [REQUIRED] - a string (or buffer) representing the key (password) that is to be checked. | ||
* paramsObject - [REQUIRED] - parameters to control scrypt hashing (see params above). | ||
* output_length - [REQUIRED] - the length of the resulting hashed output. | ||
* callback_function - [OPTIONAL] - not applicable to synchronous function. If present in async function, then it will be treated as a normal async callback. If not present, a Promise will be returned if ES6 promises are available. If not present and ES6 promises are not present, a SyntaxError will be thrown. | ||
Bytes 0 to 63 are left in plaintext. This is necessary as these bytes contain metadata needed for verifying the hash. This information not being encrypted does not mean that security is weakened. What is essential in terms of security is hash **integrity** (meaning that no part of the hashed output can be changed) and that the original password cannot be determined from the hashed output (this is why you are using scrypt - because it does this in a good way). Bytes 64 to 95 is where all this happens. | ||
# Example Usage | ||
### Verify | ||
The [verify function](#verify-1) determines whether a hash can be derived from a given key and returns a boolean result. | ||
## params | ||
### Key Derivation Function | ||
The underlying [scrypt key derivation function](#kdf). This functionality is exposed for users who are quite experienced and need the function for business logic. A good example is [litecoin](https://litecoin.org/) which uses the scrypt key derivation function as a proof of work. The key derivation function in this module is tested against [three of the four test vectors](http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00#page-11) in the original scrypt paper. The fourth test vector takes too long to computer and is infeasible to use as testing for continuous integration. Nevertheless, it is included in the tests, but commented out - uncomment it and run the tests, but be warned that it is rather taxing on resources. | ||
```JavaScript | ||
var scrypt = require("scrypt"); | ||
#### Use Hash To Store Keys | ||
If your interested in this module is to produce hashes to store passwords, then I strongly encourage you to use the hash function. The key derivation function does not produce any [message authentication code](http://en.wikipedia.org/wiki/Message_authentication_code) to ensure integrity. You will also have to store the scrypt parameters separately. Lastly, there is no native verify function included in this module. | ||
//Synchronous | ||
try { | ||
//Uses 0.1 for maxtime, and default values maxmem and maxmemfrac | ||
var scryptParameters = scrypt.paramsSync(0.1); | ||
console.log(scryptParameters); | ||
} catch(err) { | ||
//handle error | ||
} | ||
In short: If you are going to use this module to store keys, then use the hash function. It has been customized for general key storage and is both easier to use and provides better protection compared to the key derivation function. | ||
//Asynchronous with callback | ||
scrypt.params(0.1, function(err, scryptParameters) { | ||
console.log(scryptParameters); | ||
}); | ||
### Backward Compatibility For User's Of Version 1.x | ||
Four extra functions are provided for means of backward compatibility: | ||
//Asynchronous with promise | ||
scrypt.params(0.1).then(function(result){ | ||
console.log(result); | ||
}, function(err) { | ||
console.log(err); | ||
}); | ||
``` | ||
1. [passwordHash](#passwordhash) | ||
2. [passwordHashSync](#passwordhashsync) | ||
3. [verifyHash](#verifyhash) | ||
4. [verifyHashSync](#verifyhashsync) | ||
## kdf | ||
The above functions are defaulted to behave exactly like the previous version. | ||
```JavaScript | ||
var scrypt = require("scrypt"); | ||
var scryptParameters = scrypt.paramsSync(0.1); | ||
var key = new Buffer("this is a key"); //could also be a string | ||
## API | ||
##### A Note On Error Synchronous Handling | ||
All synchronous functionality should be wrapped in a `try ... catch` as exceptions are thrown in case of error. For asynchronous functionality, error are returned as the first argument to the callback function if such an error exists. An error is an object with both an error code and a message describing the error. | ||
//Synchronous example that will output in hexidecimal encoding | ||
var kdfResult = scrypt.kdfSync(key, scryptParameters); //should be wrapped in try catch, but leaving it out for brevity | ||
console.log("Synchronous result: "+kdfResult.toString("hex")); | ||
##### A Note On Error Asynchronous Handling | ||
For asynchronous functionality, an error is thrown if the error is a programmer error. For more information about different error types, see [this](https://www.joyent.com/developers/node/design/errors#) article. For example, specifying the `hashEncoding` for verify that is different to the actual hash's encoding will throw an error. | ||
//Asynchronous example that expects key to be ascii encoded | ||
scrypt.kdf("ascii encoded key", {N: 1, r:1, p:1}, function(err, result){ | ||
//Note how scrypt parameters was passed as a JSON object | ||
console.log("Asynchronous result: "+result.toString("base64")); | ||
}); | ||
On a successful result, the err object of the callback function will be `null` (not `undefined`). | ||
//Asynchronous with promise | ||
scrypt.kdf("ascii encoded key", {N: 1, r:1, p:1}).then(function(result){ | ||
console.log("Asynchronous result: "+result.toString("base64")); | ||
}, function(err){ | ||
}); | ||
``` | ||
##### Scrypt Parameter Object | ||
The scrypt parameter object is a JSON object that must have values for properties **N**, **r** and **p**. For example, it could look like this: | ||
## verifyKdf | ||
{ | ||
N: 1, | ||
r: 1, | ||
p: 1 | ||
} | ||
```JavaScript | ||
var scrypt = require("scrypt"); | ||
var scryptParameters = scrypt.paramsSync(0.1); | ||
var kdfResult = scrypt.kdfSync("password", scryptParameters); | ||
### Params | ||
`params(maxtime, maxmem, maxmemfrac, callback_function)` | ||
//Synchronous | ||
scrypt.verifyKdfSync(kdfResult, "password"); // returns true | ||
scrypt.verifyKdfSync(kdfResult, "incorrect password"); // returns false | ||
* `maxtime` - [REQUIRED] - a decimal (double) representing the maxtime in seconds for running scrypt. | ||
* `maxmem` - [OPTIONAL] - an integer, specifying the maximum number of bytes | ||
* `maxmemfrac` - [OPTIONAL] - a double value between 0.0 and 1.0, representing a normalized percentage value | ||
* `callback_function` - [OPTIONAL] - if present, will make this function asynchronous | ||
//Asynchronous | ||
scrypt.verifyKdf(kdfResult, new Buffer("password"), function(err, result) { | ||
//result will be true | ||
}); | ||
#### Params Config Object | ||
The params config object is accessible from `scrypt.params.config`. It has the following default value: | ||
//Asynchronous with promise | ||
scrypt.verifyKdf(kdfResult, "incorrect password").then(function(result) { | ||
//result will be false | ||
}, function(err) { | ||
}); | ||
``` | ||
{ | ||
maxmem: 0, | ||
maxmemfrac: 0.5 | ||
} | ||
* `maxmem` - an integer representing the default value maxmem is set to if not explicitly defined in the function call | ||
* `maxmemfrac` - a double representing the default value maxmemfrac is set to if not explicitly defined in the function call | ||
Read the section on [how memory is calculated](#how-memory-is-calculated) to get a better understanding of these values. | ||
The return value will be a [scrypt parameter object](#scrypt-parameter-object) | ||
### Hash | ||
`hash(key, scrypt_parameters, callback_function)` | ||
* `key` - [REQUIRED] - an [encoded string or buffer](#encodings) representing the key to be hashed | ||
* `scrypt_parameters` - [REQUIRED] - a JSON object representing the [scrypt's internal parameters](#params) | ||
* `callback_function` - [OPTIONAL] - if present, will make this function asynchronous | ||
#### Hash Config Object | ||
The hash config object is accessible from `scrypt.hash.config`. It has the following default value: | ||
{ | ||
keyEncoding: 'buffer', | ||
outputEncoding: 'buffer' | ||
} | ||
* `keyEncoding` - a string representing the [encoding](#encodings) of the input key | ||
* `outputEncoding` - a string representing the [encoding](#encodings) of the output returned to the user | ||
The return value will be an [encoded string or buffer](#encodings) of the [hash format](#hash-format). | ||
### Verify | ||
`verify(hash, key, callback_function)` | ||
* `hash` - [REQUIRED] - an [encoded string or buffer](#encodings) of the output of the hash function | ||
* `key` - [REQUIRED] - an [encoded string or buffer](#encodings) representing the key to be hashed | ||
* `callback_function` - [OPTIONAL] - if present, will make this function asynchronous | ||
#### Verify Config Object | ||
The verify config object is accessible from `scrypt.verify.config`. It has the following default value: | ||
{ | ||
hashEncoding: 'buffer', | ||
keyEncoding: 'buffer' | ||
} | ||
* `hashEncoding` - a string representing the [encoding](#encodings) of the input hash | ||
* `keyEncoding` - a string representing the [encoding](#encodings) of the input key | ||
The return value will be a `boolean` representing if the hash can be derived from the key | ||
### KDF | ||
`kdf(key, scrypt_parameters, outputLength, salt, callback_function)` | ||
* `key` - [REQUIRED] - an [encoded string or buffer](#encodings) representing the key to be hashed | ||
* `scrypt_parameters` - [REQUIRED] - a JSON object representing the [scrypt's internal parameters](#params) | ||
* `outputLength` - [OPTIONAL] - an integer, representing the size in bytes of the output | ||
* `salt` - [OPTIONAL] - an [encoded string or buffer](#encodings) representing the value used for salt. If not defined, a random salt will be created. | ||
* `callback_function` - [OPTIONAL] - if present, will make this function asynchronous | ||
The return value will be a JSON object with the following properties: | ||
1. **hash** - the resulting scrypt KDF hash | ||
2. **salt** - the salt used to make the hash | ||
#### KDF Config Object | ||
The kdf config object is accessible from `scrypt.kdf.config`. It has the following default value: | ||
{ | ||
saltEncoding: 'buffer', | ||
keyEncoding: 'buffer', | ||
outputEncoding: 'buffer', | ||
defaultSaltSize: 32, | ||
outputLength: 64 | ||
} | ||
* `saltEncoding` - a string representing the [encoding](#encodings) of the input salt | ||
* `keyEncoding` - a string representing the [encoding](#encodings) of the input key | ||
* `outputEncoding` - a string representing the [encoding](#encodings) of the output returned to the user | ||
* `defaultSaltSize` - an integer representing the number of bytes used to create a random salt should it be necessary | ||
* `outputLength` - an integer representing the size of the output in bytes | ||
### Backward Compatibility | ||
#### PasswordHash | ||
`passwordHash(key, maxtime, maxmem, maxmemfrac, callback_function)` | ||
* `key` - [REQUIRED] - a key string. | ||
* `maxtime` - [REQUIRED] - a decimal (double) representing the maxtime in seconds for running scrypt. Use 0.1 (100 milliseconds) for interactive login. | ||
* `maxmem` - [OPTIONAL] - instructs scrypt to use the specified number of bytes of RAM (default 0). | ||
* `maxmemfrac` - [OPTIONAL] - instructs scrypt to use the specified fraction of RAM (defaults 0.5). | ||
* `callback_function` - [Optional] - a callback function that will handle processing when result is ready. If this argument is not present, the function will behave in a synchronous manner like the function below. | ||
#### PasswordHashSync | ||
`passwordHashSync(key, maxtime, maxmem, maxmemfrac)` | ||
* `key` - [REQUIRED] - a password string. | ||
* `maxtime` - [REQUIRED] - a decimal (double) representing the maxtime in seconds for running scrypt. Use 0.1 (100 milliseconds) for interactive logins. | ||
* `maxmem` - [OPTIONAL] - instructs scrypt to use the specified number of bytes of RAM (default 0). | ||
* `maxmemfrac` - [OPTIONAL] - instructs scrypt to use the specified fracion of RAM (defaults 0.5). | ||
#### verifyHash | ||
`verifyHash(hash, key, callback_function)` | ||
* `hash` - [REQUIRED] - the password created with the above `passwordHash` function. | ||
* `key` - [REQUIRED] - a password string. | ||
* `callback_function` - [OPTIONAL] - a callback function that will handle processing when result is ready. If this argument is not present, the function will behave in a synchronous manner like the function below | ||
#### verifyHashSync | ||
`verifyHashSync(hash, password)` | ||
* `hash` - [REQUIRED] - the password created with the above `passwordHash` function. | ||
* `password` - [REQUIRED] - a password string. | ||
# Example Usage | ||
## params | ||
var scrypt = require("scrypt"); | ||
console.log(scrypt.params.config); //Outputs the config object to screen | ||
//Synchronous | ||
try { | ||
//Uses 0.1 for maxtime, and the values in the config object for maxmem and maxmemfrac | ||
var scryptParameters = scrypt.params(0.1); | ||
console.log(scryptParameters); | ||
} catch(err) { | ||
} | ||
//Asynchronous | ||
scrypt.params(0.1, function(err, scryptParameters) { | ||
console.log(scryptParameters); | ||
}); | ||
## hash | ||
var scrypt = require("scrypt"); | ||
var scryptParameters = scrypt.params(0.1); | ||
var key = new Buffer("this is a key"); //key defaults to buffer in config, so input must be a buffer | ||
//Synchronous example that will output in hexidecimal encoding | ||
scrypt.hash.config.outputEncoding = "hex"; | ||
var hash = scrypt.hash(key, scryptParameters); //should be wrapped in try catch, but leaving it out for brevity | ||
console.log("Synchronous result: "+hash); | ||
//Asynchronous example that expects key to be ascii encoded | ||
scrypt.hash.config.keyEncoding = "ascii"; | ||
scrypt.hash("ascii encoded key", {N: 1, r:1, p:1}, function(err, result){ | ||
//result will be hex encoded | ||
//Note how scrypt parameters was passed as a JSON object | ||
console.log("Asynchronous result: "+result); | ||
}); | ||
## verify | ||
var scrypt = require("scrypt"); | ||
var scryptParameters = scrypt.params(0.1); | ||
scrypt.hash.config.keyEncoding = "ascii"; | ||
scrypt.verify.config.keyEncoding = "ascii"; | ||
var hash = scrypt.hash("password", scryptParameters); | ||
//Synchronous | ||
scrypt.verify(hash, "password"); //result will be true | ||
scrypt.verify(hash, "incorrect password"); //result will be false | ||
//Asynchronous | ||
scrypt.verify(hash, "password", function(err, result) { | ||
//result will be true | ||
}); | ||
## kdf | ||
The [scrypt paper](http://www.tarsnap.com/scrypt/scrypt.pdf) lists four [test vectors](http://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00#page-11) to test implementation. This example will show how to produce these test vectors from within this module. | ||
#### Test Vector 1 | ||
### Test Vector 1 | ||
var scrypt = require("scrypt"); | ||
scrypt.kdf.config.saltEncoding = "ascii"; | ||
var key = new Buffer(""); | ||
```JavaScript | ||
var scrypt = require("scrypt"); | ||
var key = new Buffer(""); | ||
//Synchronous | ||
var res = scrypt.kdf(key,{"N":16,"r":1,"p":1},64,""); | ||
console.log(res.hash.toString("hex")); | ||
//Synchronous | ||
var result = scrypt.hashSync(key,{"N":16,"r":1,"p":1},64,""); | ||
console.log(result.toString("hex")); | ||
//Asynchronous | ||
scrypt.kdf(key, {"N":16,"r":1,"p":1},64,"", function(err, res) { | ||
console.log(res.hash.toString("hex")); | ||
}); | ||
//Asynchronous | ||
scrypt.hash(key, {"N":16,"r":1,"p":1},64,"", function(err, res) { | ||
console.log(result.toString("hex")); | ||
}); | ||
#### Test Vector 2 | ||
//Asynchronous with promise | ||
scrypt.hash(key, {"N":16,"r":1,"p":1},64,"").then(function(result) { | ||
console.log(result.toString("hex")); | ||
}, function(err){}); | ||
``` | ||
var scrypt = require("scrypt"); | ||
scrypt.kdf.config.keyEncoding = "ascii"; | ||
var salt = new Buffer("NaCl"); | ||
### Test Vector 2 | ||
//Synchronous | ||
var res = scrypt.kdf("password",{"N":1024,"r":8,"p":16},64,salt); | ||
console.log(res.hash.toString("hex")); | ||
```JavaScript | ||
var scrypt = require("scrypt"); | ||
var salt = new Buffer("NaCl"); | ||
scrypt.kdf("password", {"N":1024,"r":8,"p":16},64,salt, function(err, res) { | ||
console.log(res.hash.toString("hex")); | ||
}); | ||
//Synchronous | ||
var result = scrypt.hashSync("password", {"N":1024,"r":8,"p":16}, 64, salt); | ||
console.log(result.toString("hex")); | ||
scrypt.hash("password", {"N":1024,"r":8,"p":16},64,salt, function(err, result) { | ||
console.log(result.toString("hex")); | ||
}); | ||
``` | ||
#### Test Vector 3 | ||
var scrypt = require("scrypt"); | ||
scrypt.kdf.config.outputEncoding = "hex"; | ||
var key = new Buffer("pleaseletmein"); | ||
var salt = new Buffer("SodiumChloride"); | ||
### Test Vector 3 | ||
//Synchronous | ||
var res = scrypt.kdf(key,{"N":16384,"r":8,"p":1},64,salt); | ||
console.log(res.hash); | ||
```JavaScript | ||
var scrypt = require("scrypt"); | ||
var key = new Buffer("pleaseletmein"); | ||
var salt = new Buffer("SodiumChloride"); | ||
//Asynchronous | ||
scrypt.kdf(key, {"N":16384,"r":8,"p":1},64,salt, function(err, res) { | ||
console.log(res.hash); | ||
}); | ||
//Synchronous | ||
var result = scrypt.hashSync(key,{"N":16384,"r":8,"p":1},64,salt); | ||
console.log(result.toString("hex")); | ||
//Asynchronous | ||
scrypt.hash(key, {"N":16384,"r":8,"p":1}, 64, salt, function(err, result) { | ||
console.log(result.toString("hex")); | ||
}); | ||
``` | ||
#### Test Vector 4 | ||
### Test Vector 4 | ||
Note: This test vector is very taxing in terms of resources. | ||
var scrypt = require("scrypt"); | ||
scrypt.kdf.config.saltEncoding = "ascii"; | ||
scrypt.kdf.config.keyEncoding = "ascii"; | ||
//Synchronous | ||
var res = scrypt.kdf("pleaseletmein",{"N":1048576,"r":8,"p":1},64,"SodiumChloride"); | ||
console.log(res.hash.toString("hex")); | ||
```JavaScript | ||
var scrypt = require("scrypt"); | ||
//Asynchronous | ||
scrypt.kdf("pleaseletmein", {"N":1048576,"r":8,"p":1},64,"SodiumChloride", function(err, res) { | ||
console.log(res.hash.toString("hex")); | ||
}); | ||
//Synchronous | ||
var result = scrypt.hashSync("pleaseletmein",{"N":1048576,"r":8,"p":1},64,"SodiumChloride"); | ||
console.log(result.toString("hex")); | ||
## Backward Compatibility Functions | ||
These examples illustrate how to use the backward compatibility functions. | ||
### Asynchronous Authentication And Verification | ||
For interactive authentication, set `maxtime` to `0.1` - 100 milliseconds (although you should ensure that 100 milliseconds on your hardware is sufficiently secure). | ||
//Asynchronous | ||
scrypt.hash("pleaseletmein", {"N":1048576,"r":8,"p":1},64,"SodiumChloride", function(err, result) { | ||
console.log(result.toString("hex")); | ||
}); | ||
``` | ||
#### To create a password hash | ||
var scrypt = require("scrypt"); | ||
var password = "This is a password"; | ||
var maxtime = 0.1; | ||
# FAQ | ||
## General | ||
### What Platforms Are Supported? | ||
This module supports most posix platforms, as well as Microsoft Windows. It has been tested on the | ||
following platforms: **Linux**, **MAC OS**, **SmartOS** (so its ready for Joyent Cloud) | ||
and **Microsoft Windows**. It also works on FreeBSD, OpenBSD, SunOS etc. | ||
scrypt.passwordHash(password, maxtime, function(err, pwdhash) { | ||
if (!err) { | ||
//pwdhash should now be stored in the database | ||
} | ||
}); | ||
## Scrypt | ||
### Why Use Scrypt? | ||
Note: `maxmem` and `maxmemfrac` can also be passed to hash function. If they are not passed, then `maxmem` defaults to `0` and `maxmemfrac` defaults to `0.5`. If these values are to be passed, then they must be passed after `maxtime` and before the callback function like so: | ||
var scrypt = require("scrypt"); | ||
var password = "This is a password"; | ||
var maxtime = 0.1; | ||
var maxmem = 0, maxmemfrac = 0.5; | ||
It is probably the most advanced key derivation function available. This is is quote taken | ||
from a comment in hacker news: | ||
scrypt.passwordHash(password, maxtime, maxmem, maxmemfrac, function(err, pwdhash) { | ||
if (!err) { | ||
//pwdhash should now be stored in the database | ||
} | ||
}); | ||
>Passwords hashed with scrypt with sufficiently-high strength values (there are 3 tweakable | ||
input numbers) are fundamentally impervious to being cracked. I use the word "fundamental" | ||
in the literal sense, here; even if you had the resources of a large country, you would not | ||
be able to design any hardware (whether it be GPU hardware, custom-designed hardware, or | ||
otherwise) which could crack these hashes. Ever. (For sufficiently-small definitions of | ||
"ever". At the very least "within your lifetime"; probably far longer.) | ||
#### To verify a password hash | ||
### What Are The Pros And Cons For Using Scrypt? | ||
#### Pros | ||
var scrypt = require("scrypt"); | ||
var password = "This is a password"; | ||
var hash; //This should be obtained from the database | ||
scrypt.verifyHash(hash, password, function(err, result) { | ||
if (!err) | ||
return result; //Will be True | ||
return False; | ||
}); | ||
### Synchronous Authentication And Verification | ||
Again, for interactive authentication, set `maxtime` to `0.1` - 100 milliseconds. | ||
#### To create a password hash | ||
var scrypt = require("scrypt"); | ||
var password = "This is a password"; | ||
var maxtime = 0.1; | ||
var hash = scrypt.passwordHashSync(password, maxtime); | ||
Note: `maxmem` and `maxmemfrac` can also be passed to hash function. If they are not passed, then `maxmem` defaults to `0` and `maxmemfrac` defaults to `0.5`. If these values are to be passed, then they must be passed after `maxtime` and before the callback function like so: | ||
var scrypt = require("scrypt"); | ||
var password = "This is a password"; | ||
var maxtime = 0.1; | ||
var maxmem = 0, maxmemfrac = 0.5; | ||
var hash = scrypt.passwordHashSync(password, maxtime, maxmem, maxmemfrac); | ||
#### To verify a password hash | ||
var scrypt = require("scrypt"); | ||
var password = "This is a password"; | ||
var hash; //This should be obtained from the database | ||
var result = scrypt.verifyHashSync(hash, password); | ||
Note: There is no error description for the synchronous version. Therefore, if an error occurs, it will just return its result as `false`. | ||
## FAQ | ||
### General | ||
#### What Platforms Are Supported? | ||
This module supports most posix platforms, as well as Microsoft Windows. It has been tested on the following platforms: **Linux**, **MAC OS**, **SmartOS** (so its ready for Joyent Cloud) and **Microsoft Windows**. It also works on FreeBSD, OpenBSD, SunOS etc. | ||
### Scrypt | ||
#### Why Use Scrypt? | ||
It is probably the most advanced key derivation function available. This is is quote taken from a comment in hacker news: | ||
>Passwords hashed with scrypt with sufficiently-high strength values (there are 3 tweakable input numbers) are fundamentally impervious to being cracked. I use the word "fundamental" in the literal sense, here; even if you had the resources of a large country, you would not be able to design any hardware (whether it be GPU hardware, custom-designed hardware, or otherwise) which could crack these hashes. Ever. (For sufficiently-small definitions of "ever". At the very least "within your lifetime"; probably far longer.) | ||
#### What Are The Pros And Cons For Using Scrypt | ||
##### Pros | ||
* The scrypt algorithm has been published by [IETF](http://en.wikipedia.org/wiki/IETF) as an [Internet Draft](http://en.wikipedia.org/wiki/Internet_Draft) and is thus on track to becoming a standard. See [here](https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00) for the draft. | ||
* The scrypt algorithm has been published by [IETF](http://en.wikipedia.org/wiki/IETF) | ||
as an [Internet Draft](http://en.wikipedia.org/wiki/Internet_Draft) and is thus on track to becoming a standard. See [here](https://tools.ietf.org/html/draft-josefsson-scrypt-kdf-00) for the draft. | ||
* It is being actively used in production at [Tarsnap](http://www.tarsnap.com/). | ||
@@ -526,27 +328,48 @@ * It is much more secure than bcrypt. | ||
* It is production ready. | ||
* There is a scrypt library for most major scripting languages (Python, Ruby etc). Now this module provides the library for NodeJS :) | ||
* There is a scrypt library for most major scripting languages | ||
(Python, Ruby etc). Now this module provides the library for NodeJS :) | ||
I will end this section with a quote from Colin Percival (author of scrypt): | ||
> We estimate that on modern (2009) hardware, if 5 seconds are spent computing a derived key, the cost of a hardware brute-force attack against scrypt is roughly 4000 times greater than the cost of a similar attack against bcrypt (to find the same password), and 20000 times greater than a similar attack against PBKDF2. | ||
> We estimate that on modern (2009) hardware, if 5 seconds are spent computing a derived key, | ||
the cost of a hardware brute-force attack against scrypt is roughly 4000 times greater than the | ||
cost of a similar attack against bcrypt (to find the same password), and 20000 times greater | ||
than a similar attack against PBKDF2. | ||
##### Cons | ||
There is just one con I can think of: It is a relatively new library (only been around since 2009). Cryptographers don't really like new libraries for production deployment as it has not been *battle tested*. That being said, it is being actively used in [Tarsnap](http://www.tarsnap.com/) (as mentioned above) and the author is very active. | ||
#### Cons | ||
There is just one con I can think of: It is a relatively new library (only been around since 2009). | ||
Cryptographers don't really like new libraries for production deployment as it has not been *battle | ||
tested*. That being said, it is being actively used in [Tarsnap](http://www.tarsnap.com/) | ||
(as mentioned above) and the author is very active. | ||
### Hash | ||
#### What Are The Essential Properties For Storing Passwords | ||
## Using Scrypt With Passwords | ||
### What Are The Essential Properties For Storing Passwords? | ||
Storing passwords requires three essential properties | ||
* The password must not be stored in plaintext. (Therefore it is hashed). | ||
* The password hash must be salted. (Making a rainbow table attack very difficult to pull off). | ||
* The salted hash function must not be fast. (If someone does get hold of the salted hashes, their only option will be brute force which will be very slow). | ||
* The password must not be stored in plaintext. | ||
* The password hash must be salted. (Making a rainbow table attack very | ||
difficult to pull off). | ||
* The salted hash function must not be fast. (If someone does get hold | ||
of the salted hashes, their only option will be brute force which will | ||
be very slow). | ||
As an example of how storing passwords can be done badly, take [LinkedIn](http://www.linkedin.com). In 2012, they [came under fire](http://thenextweb.com/socialmedia/2012/06/06/bad-day-for-linkedin-6-5-million-hashed-passwords-reportedly-leaked-change-yours-now/#!rS1HT) for using unsalted hashes to store their passwords. As most commentators at the time were focusing no salt being present, the big picture was missed. In fact, their biggest problem was that they used [sha1](http://en.wikipedia.org/wiki/SHA-1), a very fast hash function. | ||
As an example of how storing passwords can be done badly, take [LinkedIn](http://www.linkedin.com). | ||
In 2012, they [came under fire](http://thenextweb.com/socialmedia/2012/06/06/bad-day-for-linkedin-6-5-million-hashed-passwords-reportedly-leaked-change-yours-now/#!rS1HT) | ||
for using unsalted hashes to store their passwords. As most commentators at | ||
the time were focusing no salt being present, the big picture was missed. | ||
In fact, their biggest problem was that they used [sha1](http://en.wikipedia.org/wiki/SHA-1), | ||
a very fast hash function. | ||
This module's [hash function](#hash-1) provides all the above properties | ||
### If random salts are used, why do all resulting KDF's start with *c2NyeXB0*? | ||
The kdf has a [specific format](http://security.stackexchange.com/questions/88678/why-does-node-js-scrypt-function-use-hmac-this-way/91050#91050): | ||
The word *"scrypt"* is added as a prefix. The reason for this is because | ||
I am sticking to Colin Percival's (the creator of scrypt) reference implementation, | ||
whereby he prefixes *scrypt* in this way. The base64 encoding of the ascii *"scrypt"* | ||
is *c2NyeXB0*. The scrypt parameters are then appended. Users of scrypt normally do | ||
not change this information once it is settled upon (hence this will also look the | ||
be identical). | ||
#### If random salts are used for each hash, why does each hash start with *c2NyeXB0* when using passwordHash | ||
All hashes start with the word *"scrypt"*. The reason for this is because I am sticking to Colin Percival's (the creator of scrypt) hash reference implementation whereby he starts off each hash this way. The base64 encoding of the ascii *"scrypt"* is *c2NyeXB0*. Seeing as *passwordHash* defaults it's output to base64, every hash produced will start with *c2NyeXB0*. Next is the scrypt parameter. Users of scrypt normally do not change this information once it is settled upon (hence this will also look the same for each hash). | ||
To illustrate with an example, I have hashed two password: *password1* and *password2*. Their outputs are as follows: | ||
To illustrate with an example, I have hashed two password: *password1* and *password2*. | ||
Their Base64 outputs are as follows: | ||
@@ -556,3 +379,3 @@ password1 | ||
/HboSy1ptzN0YzHJhC7PZIEPQzf2nuoaqVZg8VkKEJlo8/QaH7qjU2VwB | ||
password2 | ||
@@ -562,3 +385,5 @@ c2NyeXB0AAwAAAAIAAAAAZ/+bp8gWcTZgEC7YQZeLLyxFeKRRdDkwbaGeFC0NkdUr/YFAWY | ||
As one can see from the above example, both hashes start off by looking similar (they both start with *c2NyeXB0AAwAAAAIAAAAA* - as explained above), but after this, things change very rapidly. In fact, I hashed the password *password1* again: | ||
As one can see from the above example, both hashes start off by looking similar (they both start | ||
with *c2NyeXB0AAwAAAAIAAAAA* - as explained above), but after this, things change very rapidly. | ||
In fact, I hashed the password *password1* again: | ||
@@ -569,11 +394,22 @@ password1 | ||
Compare this hash to the one above. Even though they start off looking similar, their outputs are vastly different (even though it is the same password being hashed). This is because of the **random** salt that has been added, ensuring that no two hashes will ever be identical, even if the password that is being hashed is the same. | ||
Compare this hash to the one above. Even though they start off looking similar, their outputs | ||
are vastly different (even though it is the same password being hashed). This is because of | ||
the **random** salt that has been added, ensuring that no two hashes will ever be identical, | ||
even if the password that is being hashed is the same. | ||
For those that are curious or paranoid, please look at how the hash is both [produced](https://github.com/barrysteyn/node-scrypt/blob/master/src/scryptwrapper/hash.c#L37-81) and [verified](https://github.com/barrysteyn/node-scrypt/blob/master/src/scryptwrapper/hash.c#L83-122) (you are going to need some knowledge of the [C language](http://c.learncodethehardway.org/book/) for this). | ||
For those that are curious or paranoid, please look at how the kdf is both [produced](https://github.com/barrysteyn/node-scrypt/blob/master/src/scryptwrapper/keyderivation.c#L36-L80) | ||
and [verified](https://github.com/barrysteyn/node-scrypt/blob/master/src/scryptwrapper/keyderivation.c#L82-L121) (you are going to need some knowledge of the [C language](http://c.learncodethehardway.org/book/) for this). | ||
## Credits | ||
The scrypt library is Colin Percival's [scrypt](http://www.tarsnap.com/scrypt.html) project. This includes the encryption/decryption functions which are basically just wrappers into this library. | ||
# Roadmap | ||
The password hash and verify functions are also very heavily influenced by the scrypt source code, with most functionality being copied from various placed within scrypt. | ||
## Incorportate version 1.2.0 | ||
Colin Percival has released a new version of scrypt, the first new version in five | ||
years. While it is not more secure than the current version, it does allow things | ||
to be done with more ease (like support for other platforms). | ||
## Better Windows support | ||
# Credits | ||
The scrypt library is Colin Percival's [scrypt](http://www.tarsnap.com/scrypt.html) project. | ||
Syed Beparey was instrumental in getting the Windows build working, with most of the Windows build based off the work done by Dinesh Shanbhag. |
@@ -1,983 +0,789 @@ | ||
var test = require('tap').test; | ||
var scrypt = require('../'); | ||
var keyString = "This is the test key"; | ||
var keyStringObject = new String(keyString); | ||
var keyBuffer = new Buffer(keyString); | ||
var message = "This is a message"; | ||
var scryptParameters = {"N":1, "r":1, "p":1} | ||
var chai = require("chai") | ||
, chaiAsPromised = require("chai-as-promised") | ||
, scrypt = require("../"); | ||
//These error results are taken verbatim from src/node-boilerplate/scrypt_common.h | ||
var JSARG=1; //Error in JavaScript land: Argument mismatch | ||
var ADDONARG=2; //Error resulting from argument mismatch in the node addon module | ||
var PARMOBJ=3; //Scrypt generated errors | ||
var SCRYPT=4; //Scrypt generated errors | ||
chai.use(chaiAsPromised); | ||
// | ||
// Logic Tests | ||
// | ||
test("KDF - Test vector 1", function(t) { | ||
var kdf = scrypt.KDF(); | ||
kdf.config.saltEncoding = "ascii"; | ||
var buf = new Buffer(""); | ||
var res = kdf(buf,{"N":16,"r":1,"p":1},64,""); | ||
t.equal(res.hash.toString("hex"),"77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906","Synchronous test: first test vector is correctly returned"); | ||
var expect = chai.expect; | ||
kdf(buf, {"N":16,"r":1,"p":1},64,"", function(err, res) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.equal(res.hash.toString("hex"),"77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906","Asynchronous test: first test vector is correctly returned"); | ||
t.end(); | ||
}); | ||
}); | ||
describe("Scrypt Node Module Tests", function() { | ||
describe("Scrypt Params Function", function() { | ||
//Examines a returned Params JSON object | ||
var examine = function(obj, err) { | ||
expect(err) | ||
.to.not.exist; | ||
test("KDF - Test vector 2", function(t) { | ||
var kdf = scrypt.KDF(); | ||
kdf.config.keyEncoding = "ascii"; | ||
var buf = new Buffer("NaCl"); | ||
var res = kdf("password",{"N":1024,"r":8,"p":16},64,buf); | ||
t.equal(res.hash.toString("hex"),"fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640","Synchronous test: second test vector is correctly returned"); | ||
expect(obj) | ||
.to.be.a("Object") | ||
.and.to.have.all.keys("N","r","p"); | ||
kdf("password", {"N":1024,"r":8,"p":16},64,buf, function(err, res) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.equal(res.hash.toString("hex"),"fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640","Asynchronous test: second test vector is correctly returned"); | ||
t.end(); | ||
}); | ||
}); | ||
expect(obj) | ||
.to.have.property("N") | ||
.and.to.be.a("Number"); | ||
test("KDF - Test vector 3", function(t) { | ||
var kdf = scrypt.KDF(); | ||
kdf.config.outputEncoding = "hex"; | ||
var buf = new Buffer("pleaseletmein"); | ||
var salt = new Buffer("SodiumChloride"); | ||
var res = kdf(buf,{"N":16384,"r":8,"p":1},64,salt); | ||
t.equal(res.hash, "7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887","Synchronous test: third test vector is correctly returned"); | ||
expect(obj) | ||
.to.have.property("r") | ||
.and.to.be.a("Number"); | ||
kdf(buf, {"N":16384,"r":8,"p":1},64,salt, function(err, res) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.equal(res.hash,"7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887","Asynchronous test: third test vector is correctly returned"); | ||
t.end(); | ||
}); | ||
}); | ||
expect(obj) | ||
.to.have.property("p") | ||
.and.to.be.a("Number"); | ||
} | ||
//test("KDF - Test vector 4", function(t) { //This test takes too long to perform for continuous integration | ||
// var res = scrypt.kdf("pleaseletmein",{"N":1048576,"r":8,"p":1},64,"SodiumChloride"); | ||
// t.equal(res.hash.toString("hex"),"2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4", "Synchronous test: fourth test vector is correctly returned"); | ||
// | ||
// scrypt.kdf("pleaseletmein", {"N":1048576,"r":8,"p":1},64,"SodiumChloride", function(err, res) { | ||
// t.equal(res.hash.toString("hex"),"2101cb9b6a511aaeaddbbe09cf70f881ec568d574a2ffd4dabe5ee9820adaa478e56fd8f4ba5d09ffa1c6d927c40f4c337304049e8a952fbcbf45c6fa77a41a4","Asynchronous test: fourth test vector is correctly returned"); | ||
// t.end(); | ||
// }); | ||
//}); | ||
describe("Synchronous functionality with incorrect arguments", function () { | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.paramsSync) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: At least one argument is needed - the maxtime$/); | ||
}); | ||
test("KDF - Random salt added by default", function(t) { | ||
var key = new Buffer("key"); | ||
var hash1 = scrypt.kdf(key, scryptParameters); | ||
var hash2 = scrypt.kdf(key, scryptParameters); | ||
t.notEqual(hash1.hash.toString(), hash2.hash.toString(), "Synchronous: hashes that are returned are not equal. This is correct due to random salt that was added"); | ||
t.notEqual(hash1.salt.toString(), hash2.salt.toString(), "Synchronous: salts that are returned are not equal"); | ||
it("Will throw a RangeError exception if maxtime argument is less than zero", function() { | ||
expect(function() { scrypt.paramsSync(-1); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: maxtime must be greater than 0$/); | ||
}); | ||
scrypt.kdf(key, scryptParameters, function(err, hash1) { | ||
scrypt.kdf(key, scryptParameters, function(err, hash2) { | ||
t.notEqual(hash1.hash.toString(), hash2.hash.toString(), "Asynchronous: hashes that are returned are not equal. This is correct due to random salt that was added"); | ||
t.notEqual(hash1.salt.toString(), hash2.salt.toString(), "Asynchronous: salts that are returned are not equal"); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
it("Will throw a TypeError exception if maxmem is not an integer", function() { | ||
expect(function() { scrypt.paramsSync(1, 2.4); }) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: maxmem must be an integer$/); | ||
}); | ||
test("KDF - Deterministic non-random salt added manually", function(t) { | ||
var key = new Buffer("key"); | ||
var salt = new Buffer("salt"); | ||
var hash1 = scrypt.kdf(key, scryptParameters, 64, salt); | ||
var hash2 = scrypt.kdf(key, scryptParameters, 64, salt); | ||
t.equal(hash1.hash.toString(), hash2.hash.toString(), "Synchronous: hashes that are returned are equal. This is correct due to non-random salt that was added"); | ||
t.equal(hash1.salt.toString(), hash2.salt.toString(), "Synchronous: salts that are returned are identical"); | ||
it("Will throw a RangeError exception if maxmem is less than 0", function() { | ||
expect(function() { scrypt.paramsSync(1, -2); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: maxmem must be greater than or equal to 0$/); | ||
}); | ||
scrypt.kdf(key, scryptParameters, 64, salt, function(err, hash1) { | ||
scrypt.kdf(key, scryptParameters, 64, salt, function(err, hash2) { | ||
t.equal(hash1.hash.toString(), hash2.hash.toString(), "Asynchronous: hashes that are returned are equal. This is correct due to non-random salt that was added"); | ||
t.equal(hash1.salt.toString(), hash2.salt.toString(), "Asynchronous: salts that are returned are identical"); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
it("Will throw a RangeError exception if max_memfrac is not between 0.0 and 1.0", function() { | ||
expect(function() { scrypt.paramsSync(1, 2, -0.1); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: max_memfrac must be between 0.0 and 1.0 inclusive$/); | ||
test("Password hashing: Salt means same keys hash to different values", function(t) { | ||
var hash1 = scrypt.passwordHashSync(keyString, scryptParameters); | ||
var hash2 = scrypt.passwordHashSync(keyString, scryptParameters); | ||
t.notEqual(hash1,hash2,"Synchronous: same keys are correctly hashed to different values due to salt"); | ||
scrypt.passwordHash(keyString, scryptParameters, function(err, hash1) { | ||
scrypt.passwordHash(keyString, scryptParameters, function(err, hash2) { | ||
t.notEqual(hash1,hash2,"Asynchronous: same keys are correctly hashed to different values due to salt"); | ||
t.end(); | ||
}); | ||
}); | ||
}); | ||
expect(function() { scrypt.paramsSync(1, 2, 1.1); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: max_memfrac must be between 0.0 and 1.0 inclusive$/); | ||
}); | ||
test("Password hashing and verifying: Same key verify and hash (Consistency test - Result Must Be True)", function(t) { | ||
hash = scrypt.passwordHash(keyString, scryptParameters); | ||
result = scrypt.verifyHash(hash, keyString); | ||
t.equal(result, true,"Synchronous: hash has been verified as true => Result Is True"); | ||
it("Will throw a TypeError if any arguments are not numbers", function() { | ||
var args = [1, 2, 0.9]; | ||
scrypt.passwordHash(keyString, scryptParameters, function(err, hash) { | ||
t.notOk(err,"Asynchronous: no error hashing result"); | ||
scrypt.verifyHash(hash, keyString, function(err, result) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.notOk(err,"Asynchronous: no error verifying hash"); | ||
t.equal(result, true,"Asynchronous: hash has been verified as true => Result Is True"); | ||
t.end(); | ||
}) | ||
}) | ||
}); | ||
for (var i=0; i < args.length; i++) { | ||
var temp = args[i]; | ||
args[i] = "not a number"; | ||
expect(function() { scrypt.paramsSync(args[0], args[1], args[2]); }) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: (maxtime|maxmem|max_memfrac) must be a number$/); | ||
test("Password hashing and verifying: Different keys do not verify (Result Must Be False)", function(t) { | ||
hash = scrypt.passwordHash(keyString, scryptParameters); | ||
result = scrypt.verifyHash(hash, "another key"); | ||
t.equal(result, false,"Synchronous: hash has been verified as false => Result Is False (as it should be)"); | ||
args[i] = temp; | ||
} | ||
}); | ||
}); | ||
scrypt.passwordHash(keyString, scryptParameters, function(err, hash) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.notOk(err,"Asynchronous: no error hashing result"); | ||
scrypt.verifyHash(hash, "another key", function(err, result) { | ||
t.ok(err,"Asynchronous: error verifying hash - because hashes are not the same (as expected)"); | ||
t.equal(result, false,"Asynchronous: hash has been verified as false => Result Is False (as it should be)"); | ||
t.end(); | ||
}) | ||
}) | ||
}); | ||
describe("Synchronous functionality with correct arguments", function() { | ||
it("Should return a JSON object when only maxtime is defined", function() { | ||
var params = scrypt.paramsSync(1); | ||
examine(params); | ||
}); | ||
// | ||
// Argument Tests | ||
// | ||
it("Should return a JSON object when only maxtime and maxmem are defined", function() { | ||
var params = scrypt.paramsSync(1, 2); | ||
examine(params); | ||
}); | ||
it("Should return a JSON object when maxtime, maxmem and max_memfrac are defined", function() { | ||
var params = scrypt.paramsSync(1, 2, 0.5); | ||
examine(params); | ||
}); | ||
}); | ||
// | ||
// Translation Function (Parameter) Tests | ||
// | ||
describe("Asynchronous functionality with incorrect arguments", function() { | ||
var promise = undefined; | ||
//General (applies to both async and sync) | ||
test("Pick Parameters (Translation function): - no arguments are present", function(t) { | ||
try { | ||
scrypt.params(); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because at one least argument is needed - in this case, no arguments were given"); | ||
t.deepEqual(err, scrypt.errorObject(ADDONARG, "at least one argument is needed - the maxtime"), "The correct message object is returned, namely:"+ JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
// Disables promises for async test (if promises are available) | ||
before(function() { | ||
if (typeof Promise !== "undefined") { | ||
promise = Promise; | ||
Promise = undefined; | ||
} | ||
}); | ||
test("Pick Parameters (Translation function): incorrect argument type", function(t) { | ||
try { | ||
scrypt.params("abc"); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because an incorrect type was passed to the function - in this case, the maxtime was passed as a string, but a number is expected"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"maxtime argument must be a number"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
// Restores promises | ||
after(function() { | ||
if (typeof Promise === "undefined" && promise) { | ||
Promise = promise; | ||
} | ||
}); | ||
test("Pick Parameters (Translation function): incorrect argument type", function(t) { | ||
try { | ||
scrypt.params(0); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because maxtime was passed as a number <= 0"); | ||
t.deepEqual(err, scrypt.errorObject(ADDONARG, "maxtime must be greater than 0"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.params) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No arguments present$/); | ||
}); | ||
test("Pick Parameters (Translation function): incorrect argument type", function(t) { | ||
try { | ||
scrypt.params(1, "abc"); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because an incorrect type was passed to the function - in this case, the maxmem was passed as a string, but a number is expected"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG, "maxmem argument must be a number"), "The correct object is returned, namely: "+ JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a SyntaxError if no callback function is present", function() { | ||
expect(function() {scrypt.params(1);}) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No callback function present, and Promises are not available$/); | ||
}) | ||
test("Pick Parameters (Translation function): incorrect argument type", function(t) { | ||
try { | ||
scrypt.params(1, 0.5, "abc"); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because an incorrect type was passed to the function - in this case, the maxmemfrac was passed as a string, but a number is expected"); | ||
t.deepEqual(err, scrypt.errorObject(ADDONARG, "max_memfrac argument must be a number"), "The correct object is returned, namely: " + JSON.stringify(err.message)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a SyntaxError if callback function is the first argument present", function() { | ||
expect(function() {scrypt.params(function(){});}) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: At least one argument is needed before the callback - the maxtime$/); | ||
}) | ||
//Asynchronous | ||
test("Pick Parameters (Asynchronous): incorrect argument - no arguments before callback", function(t) { | ||
try { | ||
scrypt.params(function(){}); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because at one least argument is needed before the callback - in this case, no arguments were given"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"at least one argument is needed before the callback - the maxtime"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a RangeError exception if maxtime argument is less than zero", function() { | ||
expect(function() { scrypt.params(-1, function(){}); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: maxtime must be greater than 0$/); | ||
}); | ||
test("Asynchronous: Pick parameters returns an object given correct inputs of just maxtime", function(t) { | ||
scrypt.params(2, function(err, parameters) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.type(parameters,"object","Returned entity is an object"); | ||
t.type(parameters.N, "number","N is present in object and is of type number"); | ||
t.type(parameters.r, "number","r is present in object and is of type number"); | ||
t.type(parameters.p, "number","p is present in object and is of type number"); | ||
t.end(); | ||
}); | ||
}); | ||
it("Will throw a TypeError exception if maxmem is not an integer", function() { | ||
expect(function() { scrypt.params(1, 2.4, function(){}); }) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: maxmem must be an integer$/); | ||
}); | ||
test("Asynchronous: Pick parameters returns an object given correct inputs of just maxtime and max_memfrac", function(t) { | ||
scrypt.params(2, 0.5, function(err, parameters) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.type(parameters,"object","Returned entity is an object"); | ||
t.type(parameters.N, "number","N is present in object and is of type number"); | ||
t.type(parameters.r, "number","r is present in object and is of type number"); | ||
t.type(parameters.p, "number","p is present in object and is of type number"); | ||
t.end(); | ||
}); | ||
}); | ||
it("Will throw a RangeError exception if maxmem is less than 0", function() { | ||
expect(function() { scrypt.params(1, -2, function(){}); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: maxmem must be greater than or equal to 0$/); | ||
}); | ||
test("Asynchronous: Pick parameters returns an object given correct inputs of maxtime, max_memfrac and maxmem", function(t) { | ||
scrypt.params(2, 0.5, 1, function(err, parameters) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.type(parameters,"object","Returned entity is an object"); | ||
t.type(parameters.N, "number","N is present in object and is of type number"); | ||
t.type(parameters.r, "number","r is present in object and is of type number"); | ||
t.type(parameters.p, "number","p is present in object and is of type number"); | ||
t.end(); | ||
}); | ||
}); | ||
it("Will throw a RangeError exception if max_memfrac is not between 0.0 and 1.0", function() { | ||
expect(function() { scrypt.params(1, 2, -0.1, function(){}); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: max_memfrac must be between 0.0 and 1.0 inclusive$/); | ||
//Synchronous | ||
test("Synchronous: Pick parameters returns an object given correct inputs of just maxtime", function(t) { | ||
parameters = scrypt.params(2); | ||
t.type(parameters,"object","Returned entity is an object"); | ||
t.type(parameters.N, "number","N is present in object and is of type number"); | ||
t.type(parameters.r, "number","r is present in object and is of type number"); | ||
t.type(parameters.p, "number","p is present in object and is of type number"); | ||
t.end(); | ||
}); | ||
expect(function() { scrypt.params(1, 2, 1.1, function(){}); }) | ||
.to.throw(RangeError) | ||
.to.match(/^RangeError: max_memfrac must be between 0.0 and 1.0 inclusive$/); | ||
}); | ||
test("Synchronous: Pick parameters returns an object given correct inputs of just maxtime and max_memfrac", function(t) { | ||
parameters = scrypt.params(2, 0.5); | ||
t.type(parameters,"object","Returned entity is an object"); | ||
t.type(parameters.N, "number","N is present in object and is of type number"); | ||
t.type(parameters.r, "number","r is present in object and is of type number"); | ||
t.type(parameters.p, "number","p is present in object and is of type number"); | ||
t.end(); | ||
}); | ||
it("Will throw a TypeError if any arguments are not numbers", function() { | ||
var args = [1, 2, 0.9]; | ||
test("Synchronous: Pick parameters returns an object given correct inputs of maxtime, max_memfrac and maxmem", function(t) { | ||
parameters = scrypt.params(2, 0.5, 1); | ||
t.type(parameters,"object","Returned entity is an object"); | ||
t.type(parameters.N, "number","N is present in object and is of type number"); | ||
t.type(parameters.r, "number","r is present in object and is of type number"); | ||
t.type(parameters.p, "number","p is present in object and is of type number"); | ||
t.end(); | ||
}); | ||
for (var i=0; i < args.length; i++) { | ||
var temp = args[i]; | ||
args[i] = "not a number"; | ||
expect(function() { scrypt.params(args[0], args[1], args[2], function(){}); }) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: (maxtime|maxmem|max_memfrac) must be a number$/); | ||
// | ||
// Password Hash Tests | ||
// | ||
args[i] = temp; | ||
} | ||
}); | ||
}); | ||
//General (both async and sync) | ||
test("Password Hash: incorrect arguments - no arguments present", function(t) { | ||
try { | ||
scrypt.passwordHash(); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because at one least two arguments are needed - in this case, no arguments were given"); | ||
t.deepEqual(err,scrypt.errorObject(JSARG,"wrong number of arguments - at least two arguments are needed - key and scrypt parameters JSON object"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Asynchronous functionality with correct arguments", function() { | ||
it("Should return a JSON object when only maxtime is defined", function(done) { | ||
scrypt.params(1, function(err, params) { | ||
examine(params, err); | ||
done(); | ||
}); | ||
}); | ||
test("Password Hash: incorrect arguments - only one argument present", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because at one least two arguments are needed - in this case, only one was present, namely the key"); | ||
t.deepEqual(err,scrypt.errorObject(JSARG,"wrong number of arguments - at least two arguments are needed - key and scrypt parameters JSON object"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Should return a JSON object when only maxtime and maxmem are defined", function(done) { | ||
scrypt.params(1, 2, function(err, params){ | ||
examine(params, err); | ||
done(); | ||
}); | ||
}); | ||
test("Password Hash: incorrect arguments - expected key is not a string", function(t) { | ||
try { | ||
scrypt.passwordHash(1232, scryptParameters); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: An error was correctly thrown because the key type was incorrect - in this case, it was of type number, but it should be of type string or buffer"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(1232, scryptParameters, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: An error was correctly thrown because the key type was incorrect - in this case, it was of type number, but it should be of type string or buffer"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Should return a JSON object when maxtime, maxmem and max_memfrac are defined", function(done) { | ||
scrypt.params(1, 2, 0.5, function(err, params){ | ||
examine(params, err); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
test("Password Hash: incorrect arguments - expected scrypt parameter object is malformed", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, {}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, it is an empty object"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"N value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, {}, function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, it is an empty object"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"N value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Promise asynchronous functionality with correct arguments", function() { | ||
if (typeof Promise !== "undefined") { | ||
it("Should return a JSON object when only maxtime is defined", function(done){ | ||
scrypt.params(1).then(function(params) { | ||
examine(params); | ||
done(); | ||
}); | ||
}); | ||
test("Password Hash: incorrect arguments - expected scrypt parameter object is malformed", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1}); | ||
} catch (err) { | ||
t.ok(err, "Synchronout test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, it has one property, only N (but r and p are also needed)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"r value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1}, function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asychronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, it has one property, only N (but r and p are also needed)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"r value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Should return a JSON object when only maxtime and maxmem are defined", function(done) { | ||
scrypt.params(1, 2).then(function(params) { | ||
examine(params); | ||
done(); | ||
}); | ||
}); | ||
test("Password Hash: incorrect arguments - expected scrypt parameter object is malformed", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"r":1}); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because the scrypt parameter object is malformed - in this case, it has two properties, only N and r (but p is also needed)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"p value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"r":1}, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asyncrhonous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, it has two properties, only N and r (but p is also needed)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"p value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Should return a JSON object when maxtime, maxmem and max_memfrac are defined", function(done) { | ||
scrypt.params(1, 2, 0.5).then(function(params) { | ||
examine(params); | ||
done(); | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
test("Password Hash: incorrect arguments - expected scrypt parameter object is malformed", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"p":1}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, it has two properties, only N and p (but r is also needed)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"r value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"p":1}, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, it has two properties, only N and p (but r is also needed)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"r value is not present"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Scrypt KDF Function", function() { | ||
describe("Synchronous functionality with incorrect arguments", function(){ | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.kdfSync) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: At least two arguments are needed - the key and the Scrypt paramaters object$/); | ||
}); | ||
test("Password Hash: incorrect arguments - expected scrypt parameter object is malformed", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, {"N":"hello","r":1, "p":1}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, N type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"N must be a numeric value"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, {"N":"hello","r":1, "p":1},function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, N type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"N must be a numeric value"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a TypeError if the key is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.kdfSync(1123, {N:1, r:1, p:1})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Key type is incorrect: It can only be of type string or Buffer$/); | ||
}) | ||
test("Password Hash: incorrect arguments - expected scrypt parameter object is malformed", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"r":"hello", "p":1}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, r type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"r must be a numeric value"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"r":"hello", "p":1}, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, r type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"r must be a numeric value"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a TypeError if the Scrypt params object is incorrect", function() { | ||
expect(function(){scrypt.kdfSync("password", {N:1, p:1})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Scrypt params object does not have 'r' property present$/); | ||
}) | ||
}); | ||
test("Password Hash: incorrect arguments - expected scrypt parameter object is malformed", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"r":1, "p":"hello"}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, p type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"p must be a numeric value"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, {"N":1,"r":1, "p":"hello"}, function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, p type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(PARMOBJ,"p must be a numeric value"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Synchronous functionality with correct arguments", function() { | ||
it("Will return a buffer object containing the KDF with a string input", function() { | ||
var result = scrypt.kdfSync("password", {N:1, r:1, p:1}); | ||
expect(result) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result) | ||
.to.have.length.above(0); | ||
}); | ||
}); | ||
test("Password Hash: incorrect arguments - maxtime not a number", function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, "hello world"); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, p type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(JSARG,"expecting maxtime as a number"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(keyString, "hello world", function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the scrypt parameter object is malformed - in this case, p type is a string (it should be a numeric)"); | ||
t.deepEqual(err,scrypt.errorObject(JSARG,"expecting maxtime as a number"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Asyncrhonous functionality with incorrect arguments", function() { | ||
var promise = undefined; | ||
test("Password Hash: incorrect arguments - key string is empty", function(t) { | ||
try { | ||
scrypt.passwordHash("", scryptParameters); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the key string was empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash("", scryptParameters, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the key string was empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
// Disables promises for async test (if promises are available) | ||
before(function() { | ||
if (typeof Promise !== "undefined") { | ||
promise = Promise; | ||
Promise = undefined; | ||
} | ||
}); | ||
test("Password Hash: incorrect arguments - key string object is empty", function(t) { | ||
try { | ||
scrypt.passwordHash(new String(""), scryptParameters); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test: an error was correctly thrown because the key string was empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(new String(""), scryptParameters, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the key string was empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
// Restores promises | ||
after(function() { | ||
if (typeof Promise === "undefined" && promise) { | ||
Promise = promise; | ||
} | ||
}); | ||
test("Password Hash: incorrect arguments - key buffer is empty", function(t) { | ||
try { | ||
scrypt.passwordHash(new Buffer(""), scryptParameters); | ||
} catch (err) { | ||
t.ok(err, "synchronous test: an error was correctly thrown because the key string was empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
try { | ||
scrypt.passwordHash(new Buffer(""), scryptParameters, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test: an error was correctly thrown because the key string was empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.kdf) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No arguments present$/); | ||
}); | ||
test("Password Hash: incorrect arguments - only two arguments present, key and callback function" , function(t) { | ||
try { | ||
scrypt.passwordHash(keyString, function(){}); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because there was no scrypt parameters object"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"wrong number of arguments at least two arguments are needed before the callback function - key and scrypt parameters JSON object"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a SyntaxError if no callback function is present", function() { | ||
expect(function() {scrypt.kdf(new Buffer("password"), {N:1, r:1, p:1});}) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No callback function present, and Promises are not available$/); | ||
}) | ||
//Synchronous | ||
test("Password Hash (Synchronous): hash key with correct arguments: key string and scrypt parameters object", function(t) { | ||
var hash = scrypt.passwordHash(keyString, scryptParameters); | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
it("Will throw a TypeError if the key is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.kdf(1123, {N:1, r:1, p:1}, function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Key type is incorrect: It can only be of type string or Buffer$/); | ||
}) | ||
test("Password Hash (Synchronous): hash key with correct arguments: key string and maxtime number", function(t) { | ||
var hash = scrypt.passwordHash(keyString, 1); | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
it("Will throw a TypeError if the Scrypt params object is incorrect", function() { | ||
expect(function(){scrypt.kdf("password", {N:1, r:1}, function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Scrypt params object does not have 'p' property present$/); | ||
}) | ||
}); | ||
test("Password Hash (Synchronous): hash key with correct arguments: key string, maxtime number and maxmem number", function(t) { | ||
var hash = scrypt.passwordHash(keyString, 1, 0.05); | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
describe("Asynchronous functionality with correct arguments", function() { | ||
it("Will return a buffer object containing the KDF with a buffer input", function(done) { | ||
scrypt.kdf(new Buffer("password"), {N:1, r:1, p:1}, function(err, result) { | ||
expect(result) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result) | ||
.to.have.length.above(0); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
test("Password Hash (Synchronous): hash key with correct arguments: key string,maxtime number, maxmemnumber and maxmem_frac number", function(t) { | ||
var hash = scrypt.passwordHash(keyString, 1, 0.05, 0.05); | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
describe("Promise asynchronous functionality with correct arguments", function() { | ||
if (typeof Promise !== "undefined") { | ||
it("Will return a buffer object containing the KDF with a buffer input", function(done) { | ||
scrypt.kdf(new Buffer("password"), {N:1, r:1, p:1}).then(function(result) { | ||
expect(result) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result) | ||
.to.have.length.above(0); | ||
done(); | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
test("Password Hash (Synchronous): hash key with correct arguments: key string object and scrypt parameters object", function(t) { | ||
var hash = scrypt.passwordHash(keyStringObject, scryptParameters); | ||
t.ok(true, "The key was hashed successfully with a string object, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
describe("Scrypt Hash Function", function() { | ||
describe("Create Hash", function() { | ||
describe("Synchronous functionality with incorrect arguments", function() { | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.hashSync) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: At least four arguments are needed - the key to hash, the scrypt params object, the output length of the hash and the salt$/); | ||
}); | ||
test("Password Hash (Synchronous): hash key with correct arguments: key buffer and scrypt parameters object", function(t) { | ||
var hash = scrypt.passwordHash(keyBuffer, scryptParameters); | ||
t.ok(true, "The key was hashed successfully with a buffer, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
it("Will throw a TypeError if the key is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.hashSync(1123, {N:1, r:1, p:1}, 32, "NaCl")}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Key type is incorrect: It can only be of type string or Buffer$/); | ||
}) | ||
it("Will throw a TypeError if the Scrypt params object is incorrect", function() { | ||
expect(function(){scrypt.hashSync("hash something", {N:1, r:1}, 32, "NaCl")}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Scrypt params object does not have 'p' property present$/); | ||
}) | ||
//Asynchronous | ||
test("Password Hash (Asynchronous): hash key with correct arguments: key string and scrypt parameters object", function(t) { | ||
scrypt.passwordHash(keyString, scryptParameters, function(err, hash) { | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
}); | ||
it("Will throw a TypeError if the hash length is not an integer", function() { | ||
expect(function(){scrypt.hashSync("hash something", {N:1, r:1, p:1}, 32.5, new Buffer("NaCl"))}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Hash length must be an integer$/); | ||
test("Password Hash (Asynchronous): hash key with correct arguments: key string and maxtime number", function(t) { | ||
scrypt.passwordHash(keyString, 1, function(err, hash) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
}); | ||
expect(function(){scrypt.hashSync("hash something", {N:1, r:1, p:1}, "thirty-two", "NaCl")}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Hash length must be an integer$/); | ||
}) | ||
test("Password Hash (Aynchronous): hash key with correct arguments: key string, maxtime number and maxmem number", function(t) { | ||
scrypt.passwordHash(keyString, 1, 0.05, function(err, hash) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
}); | ||
it("Will throw a TypeError if the salt is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.hashSync("hash something", {N:1, r:1, p:1}, 32, 45)}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Salt type is incorrect: It can only be of type string or Buffer$/); | ||
}) | ||
}); | ||
test("Password Hash (Asynchronous): hash key with correct arguments: key string,maxtime number, maxmemnumber and maxmem_frac number", function(t) { | ||
scrypt.passwordHash(keyString, 1, 0.05, 0.05, function(err, hash) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.ok(true, "The key was hashed successfully, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
}); | ||
describe("Synchronous functionality with correct arguments", function() { | ||
var hash_length = Math.floor(Math.random() * 100) + 1; //Choose random number between 1 and 100 | ||
it("Will return a buffer object containing the hash with a string input", function() { | ||
var result = scrypt.hashSync("hash something", {N:1, r:1, p:1}, hash_length, "NaCl"); | ||
expect(result) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result) | ||
.to.have.length(hash_length); | ||
}); | ||
}); | ||
test("Password Hash (Asynchronous): hash key with correct arguments: key string object and scrypt parameters object", function(t) { | ||
scrypt.passwordHash(keyStringObject, scryptParameters, function(err, hash) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.ok(true, "The key was hashed successfully with a string object, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
}); | ||
describe("Asynchronous functionality with incorrect arguments", function() { | ||
var promise = undefined; | ||
test("Password Hash (Asynchronous): hash key with correct arguments: key buffer and scrypt parameters object", function(t) { | ||
scrypt.passwordHash(keyBuffer, scryptParameters, function (err, hash) { | ||
t.deepEqual(err, null, "Asynchronous test: err object is correctly set as null"); | ||
t.ok(true, "The key was hashed successfully with a buffer, as expected"); | ||
t.type(hash, "string", "The hash that was returned is of type 'string', as expected (because it is base64 encoded)"); | ||
t.end(); | ||
}); | ||
}); | ||
// Disables promises for async test (if promises are available) | ||
before(function() { | ||
if (typeof Promise !== "undefined") { | ||
promise = Promise; | ||
Promise = undefined; | ||
} | ||
}); | ||
// Restores promises | ||
after(function() { | ||
if (typeof Promise === "undefined" && promise) { | ||
Promise = promise; | ||
} | ||
}); | ||
// | ||
// Password Verify | ||
// | ||
test("Password Verify: incorrect arguments - no arguments present", function(t) { | ||
try { | ||
scrypt.verifyHash(); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because at one least two arguments are needed - in this case, no arguments were given"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"both hash and key are needed"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.hash) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No arguments present$/); | ||
}); | ||
test("Password Verify: incorrect arguments - one argument present", function(t) { | ||
try { | ||
scrypt.verifyHash("hash"); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because at one least two arguments are needed - in this case, no arguments were given"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"both hash and key are needed"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a SyntaxError if no callback function is present", function() { | ||
expect(function() {scrypt.hash("hash something", {N:1, r:1, p:1}, 64, "NaCl");}) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No callback function present, and Promises are not available$/); | ||
}) | ||
test("Password Verify: incorrect argument type - two arguments present, but one of them is a callback", function(t) { | ||
try { | ||
scrypt.verifyHash("hash", function(){}); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because at one least two arguments are needed - in this case, no arguments were given"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"both hash and key are needed before the callback function"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a TypeError if the key is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.hash(1123, {N:1, r:1, p:1}, 32, "NaCl", function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Key type is incorrect: It can only be of type string or Buffer$/); | ||
}) | ||
test("Password Verify: incorrect argument type - hash not a string nor a string object nor a buffer", function(t) { | ||
try { | ||
scrypt.verifyHash(123, "string"); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because hash is not a recognised type, namely string, string object or buffer. In this case, hash is a number"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Will throw a TypeError if the Scrypt params object is incorrect", function() { | ||
expect(function(){scrypt.hash("hash something", {N:1, r:1}, 32, "NaCl", function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Scrypt params object does not have 'p' property present$/); | ||
}) | ||
try { | ||
scrypt.verifyHash(123, "string", function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because hash is not a recognised type, namely string, string object or buffer. In this case, hash is a number"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a TypeError if the hash length is not an integer", function() { | ||
expect(function(){scrypt.hash("hash something", {N:1, r:1, p:1}, 32.5, new Buffer("NaCl"), function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Hash length must be an integer$/); | ||
test("Password Verify: incorrect argument type - hash is an object, but not a buffer nor a string object", function(t) { | ||
try { | ||
scrypt.verifyHash({}, "string"); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because hash is not a recognised type, namely string, string object or buffer. In this case, hash is an empty JSON object"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
expect(function(){scrypt.hash("hash something", {N:1, r:1, p:1}, "thirty-two", "NaCl", function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Hash length must be an integer$/); | ||
}) | ||
try { | ||
scrypt.verifyHash({}, "string", function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because hash is not a recognised type, namely string, string object or buffer. In this case, hash is an empty JSON object"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a TypeError if the salt is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.hash("hash something", {N:1, r:1, p:1}, 32, 45, function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Salt type is incorrect: It can only be of type string or Buffer$/); | ||
}) | ||
}); | ||
test("Password Verify: incorrect argument type - hash an empty string", function(t) { | ||
try { | ||
scrypt.verifyHash("", keyString); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the hash string is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
describe("Asynchronous functionality with correct arguments", function() { | ||
var hash_length = Math.floor(Math.random() * 100) + 1; //Choose random number between 1 and 100 | ||
it("Will return a buffer object containing the hash with a string input", function(done) { | ||
scrypt.hash("hash something", {N:1, r:1, p:1}, hash_length, "NaCl", function(err, result){ | ||
expect(result) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result) | ||
.to.have.length(hash_length); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
try { | ||
scrypt.verifyHash("", "string", function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the hash string is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Promise asynchronous functionality with correct arguments", function() { | ||
if (typeof Promise !== "undefined") { | ||
var hash_length = Math.floor(Math.random() * 100) + 1; //Choose random number between 1 and 100 | ||
it("Will return a buffer object containing the hash with a string input", function(done) { | ||
scrypt.hash("hash something", {N:1, r:1, p:1}, hash_length, "NaCl").then(function(result){ | ||
expect(result) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result) | ||
.to.have.length(hash_length); | ||
done(); | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
test("Password Verify: incorrect argument type - hash an empty string object", function(t) { | ||
try { | ||
scrypt.verifyHash(new String(""), keyString); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the hash string object is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
describe("Verify Hash", function() { | ||
describe("Synchronous functionality with incorrect arguments", function() { | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.verifyKdfSync) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: At least two arguments are needed - the KDF and the key$/); | ||
}); | ||
try { | ||
scrypt.verifyHash(new String(""), "string", function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the hash string object is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a TypeError if the KDF is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.verifyKdfSync(1232,"key")}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: KDF type is incorrect: It can only be of type string or Buffer$/); | ||
}); | ||
test("Password Verify: incorrect argument type - hash an empty buffer", function(t) { | ||
try { | ||
scrypt.verifyHash(new Buffer(""), "string"); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the hash buffer is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Will throw a TypeError if the key is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.verifyKdfSync("KDF", 1232)}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Key type is incorrect: It can only be of type string or Buffer$/); | ||
}); | ||
try { | ||
scrypt.verifyHash(new Buffer(""), "string", function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the hash buffer is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"hash cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw an Error if KDF buffer is not a valid scrypt-encrypted block", function() { | ||
expect(function(){scrypt.verifyKdfSync("KDF", "key")}) | ||
.to.throw(Error) | ||
.to.match(/^Error: data is not a valid scrypt-encrypted block$/); | ||
}); | ||
}); | ||
test("Password Verify: incorrect argument type - key not a string nor a string object nor a buffer", function(t) { | ||
try { | ||
scrypt.verifyHash("hash", 123); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because key is not a recognised type, namely string, string object or buffer. In this case, key is a number"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
describe("Synchronous functionality with correct arguments", function() { | ||
var key = "kdf" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}); | ||
try { | ||
scrypt.verifyHash("hash", 123, function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because key is not a recognised type, namely string, string object or buffer. In this case, key is a number"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will produce a boolean value", function(){ | ||
expect(scrypt.verifyKdfSync(kdf, key)) | ||
.to.be.a('boolean'); | ||
test("Password Verify: incorrect argument type - key is an object, but not a buffer nor a string object", function(t) { | ||
try { | ||
scrypt.verifyHash("hash",{}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because key is not a recognised type, namely string, string object or buffer. In this case, key is an empty JSON object"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
expect(scrypt.verifyKdfSync(kdf, "different key")) | ||
.to.be.a('boolean'); | ||
}); | ||
}); | ||
try { | ||
scrypt.verifyHash("hash", {}, function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because hash is not a recognised type, namely string, string object or buffer. In this case, key is an empty JSON object"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Asynchronous functionality with incorrect arguments", function() { | ||
var promise = undefined; | ||
test("Password Verify: incorrect argument type - key an empty string", function(t) { | ||
try { | ||
scrypt.verifyHash("hash", ""); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the key string is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
// Disables promises for async test (if promises are available) | ||
before(function() { | ||
if (typeof Promise !== "undefined") { | ||
promise = Promise; | ||
Promise = undefined; | ||
} | ||
}); | ||
try { | ||
scrypt.verifyHash("hash", "", function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the key string is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
// Restores promises | ||
after(function() { | ||
if (typeof Promise === "undefined" && promise) { | ||
Promise = promise; | ||
} | ||
}); | ||
test("Password Verify: incorrect argument type - key an empty string object", function(t) { | ||
try { | ||
scrypt.verifyHash("hash", new String("")); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the key string object is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Will throw SyntexError exception if called without arguments", function () { | ||
expect(scrypt.verifyKdf) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No arguments present$/); | ||
}); | ||
try { | ||
scrypt.verifyHash("hash", new String(""), function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the key string object is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a SyntaxError if no callback function is present", function() { | ||
var key = "kdf" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}); | ||
test("Password Verify: incorrect argument type - key an empty buffer", function(t) { | ||
try { | ||
scrypt.verifyHash("hash", new Buffer("")); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the key buffer is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
expect(function() {scrypt.verifyKdf(kdf, key);}) | ||
.to.throw(SyntaxError) | ||
.to.match(/^SyntaxError: No callback function present, and Promises are not available$/); | ||
}) | ||
try { | ||
scrypt.verifyHash("hash", new Buffer(""), function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the key buffer is empty"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key cannot be empty"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw a TypeError if the KDF is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.verifyKdf(1232,"key", function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: KDF type is incorrect: It can only be of type string or Buffer$/); | ||
}); | ||
it("Will throw a TypeError if the key is not a string or a Buffer object", function() { | ||
expect(function(){scrypt.verifyKdfSync("KDF", 1232, function(){})}) | ||
.to.throw(TypeError) | ||
.to.match(/^TypeError: Key type is incorrect: It can only be of type string or Buffer$/); | ||
}); | ||
// | ||
// Scrypt KDF Tests | ||
// | ||
test("Scrypt KDF: Incorrect arguments - no arguments present", function(t) { | ||
try { | ||
scrypt.kdf(); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because there were no arguments present"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"at least two arguments are needed - key and a json object representing the scrypt parameters"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Will throw an Error if KDF buffer is not a valid scrypt-encrypted block", function() { | ||
expect(function(){scrypt.verifyKdfSync("KDF", "key", function(){})}) | ||
.to.throw(Error) | ||
.to.match(/^Error: data is not a valid scrypt-encrypted block$/); | ||
}); | ||
}); | ||
test("Scrypt KDF: Incorrect arguments - callback in incorrect position", function(t) { | ||
try { | ||
scrypt.kdf("string",function(){}); | ||
} catch (err) { | ||
t.ok(err, "An error was correctly thrown because the callback function preceeded other needed arguments"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"at least two arguments are needed before the callback function - key and a json object representing the scrypt parameters"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Asynchronous functionality with correct arguments", function() { | ||
var key = "kdf" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}); | ||
test("Scrypt KDF: Incorrect arguments - key is not of type string, string object nor buffer", function(t) { | ||
try { | ||
scrypt.kdf(123, scryptParameters); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the key is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Will produce a boolean value", function(done){ | ||
scrypt.verifyKdf(kdf, key, function(err, result) { | ||
expect(result) | ||
.to.be.a('boolean') | ||
.to.equal(true); | ||
expect(err) | ||
.to.not.exist; | ||
try { | ||
scrypt.kdf(123, scryptParameters, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the key is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
scrypt.verifyKdf(kdf, "different key", function(err, result) { | ||
expect(result) | ||
.to.be.a('boolean') | ||
.to.equal(false); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
test("Scrypt KDF: Incorrect arguments - key is an object, not a string object nor a buffer", function(t) { | ||
try { | ||
scrypt.kdf({}, scryptParameters); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the key is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
describe("Promise asynchronous functionality with correct arguments", function() { | ||
var key = "kdf" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}); | ||
try { | ||
scrypt.kdf({}, scryptParameters, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the key is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
if (typeof Promise !== "undefined") { | ||
it("Will produce a boolean value", function(done){ | ||
scrypt.verifyKdf(kdf, key).then(function(result) { | ||
expect(result) | ||
.to.be.a('boolean') | ||
.to.equal(true); | ||
test("Scrypt KDF: Incorrect arguments - scrypt parameters is not an object", function(t) { | ||
var kdf = new scrypt.KDF(); | ||
kdf.config.keyEncoding = "ascii"; | ||
try { | ||
kdf("key", 123); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the scrypt parameters JSON object is passed as a number"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"expecting JSON object representing scrypt parameters"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
scrypt.verifyKdf(kdf, "different key").then(function(result) { | ||
expect(result) | ||
.to.be.a('boolean') | ||
.to.equal(false); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
} | ||
}); | ||
}); | ||
}); | ||
try { | ||
kdf("key", 123, function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the scrypt parameters JSON object is passed as a number"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"expecting JSON object representing scrypt parameters"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
describe("Logic", function() { | ||
describe("Test vectors", function() { | ||
describe("Synchronous", function() { | ||
it("Vector 1: Will produce an identical vector to scrypt paper", function() { | ||
var result = scrypt.hashSync("", {"N":16,"r":1,"p":1}, 64, ""); | ||
expect(result.toString("hex")) | ||
.to.equal("77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906"); | ||
}) | ||
test("Scrypt KDF: Incorrect arguments - outputLength is not a number", function(t) { | ||
var kdf = new scrypt.KDF(); | ||
kdf.config.keyEncoding = "ascii"; | ||
try { | ||
kdf("key", scryptParameters, "this should be a number"); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the size parameter was of type string"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"outputLength must be a number"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Vector 2: Will produce an identical vector to scrypt paper", function() { | ||
var result = scrypt.hashSync("password",{"N":1024,"r":8,"p":16},64, new Buffer("NaCl")); | ||
expect(result.toString("hex")) | ||
.to.equal("fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640"); | ||
}) | ||
try { | ||
kdf("key", scryptParameters, "this should be a number", function() {}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the size parameter was of type string"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"outputLength must be a number"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Vector 3: Will produce an identical vector to scrypt paper", function() { | ||
var result = scrypt.hashSync(new Buffer("pleaseletmein"),{"N":16384,"r":8,"p":1},64, "SodiumChloride"); | ||
expect(result.toString("hex")) | ||
.to.equal("7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887"); | ||
}) | ||
}); | ||
describe("Aynchronous", function() { | ||
it("Vector 1: Will produce an identical vector to scrypt paper", function(done) { | ||
scrypt.hash("", {"N":16,"r":1,"p":1}, 64, "", function(err, result) { | ||
expect(result.toString("hex")) | ||
.to.equal("77d6576238657b203b19ca42c18a0497f16b4844e3074ae8dfdffa3fede21442fcd0069ded0948f8326a753a0fc81f17e8d3e0fb2e0d3628cf35e20c38d18906"); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
test("Scrypt KDF: Incorrect arguments - salt is not of type string, string object nor buffer", function(t) { | ||
var kdf = new scrypt.KDF(); | ||
kdf.config.keyEncoding = "ascii"; | ||
try { | ||
kdf("key", scryptParameters, 32, 123); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the salt is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"salt must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Vector 2: Will produce an identical vector to scrypt paper", function(done) { | ||
scrypt.hash(new Buffer("password"),{"N":1024,"r":8,"p":16},64, new Buffer("NaCl"), function(err, result) { | ||
expect(result.toString("hex")) | ||
.to.equal("fdbabe1c9d3472007856e7190d01e9fe7c6ad7cbc8237830e77376634b3731622eaf30d92e22a3886ff109279d9830dac727afb94a83ee6d8360cbdfa2cc0640"); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
try { | ||
kdf("key", scryptParameters, 32, 123, function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the salt is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"salt must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
it("Vector 3: Will produce an identical vector to scrypt paper", function(done) { | ||
scrypt.hash("pleaseletmein",{"N":16384,"r":8,"p":1},64, "SodiumChloride", function(err, result) { | ||
expect(result.toString("hex")) | ||
.to.equal("7023bdcb3afd7348461c06cd81fd38ebfda8fbba904f8e3ea9b543f6545da1f2d5432955613f0fcf62d49705242a9af9e61e85dc0d651e40dfcf017b45575887"); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
test("Scrypt KDF: Incorrect arguments - salt is an object, but not a string object nor a buffer", function(t) { | ||
try { | ||
var kdf = new scrypt.KDF(); | ||
kdf.config.keyEncoding = "ascii"; | ||
kdf("key", scryptParameters, 32, {}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the salt is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"salt must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
describe("Kdf Logic", function() { | ||
describe("Synchronous", function() { | ||
it("Will use random salt to ensure no two KDFs are the same, even if the keys are identical", function(){ | ||
var result1 = scrypt.kdfSync("password", {N:1, r:1, p:1}) | ||
, result2 = scrypt.kdfSync("password", {N:1, r:1, p:1}); | ||
try { | ||
kdf("key", scryptParameters, 32, {}, function(){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the salt is of an incorrect type"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"salt must be a buffer or string"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
expect(result1.toString("base64")) | ||
.to.not.equal(result2.toString("base64")); | ||
}); | ||
test("Scrypt KDF: Incorrect arguments - outputLength is less than or equal to zero", function(t) { | ||
try { | ||
var kdf = new scrypt.KDF(); | ||
kdf.config.keyEncoding = "ascii"; | ||
kdf("key", scryptParameters, 0, ""); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the outputLength was set to zero"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"outputLength must be greater than 0"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Will correctly verify hash as true if identical keys are used for kdf and verify", function(){ | ||
var key = "this is a key" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}) | ||
, result = scrypt.verifyKdfSync(kdf, key); | ||
try { | ||
kdf("key", scryptParameters, 0, "", function(err, result){}); | ||
} catch (err) { | ||
t.ok(err, "Asynchronous test - An error was correctly thrown because the outputLength was set to zero"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"outputLength must be greater than 0"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
expect(result) | ||
.to.be.a("boolean") | ||
.to.equal(true); | ||
}); | ||
test("Scrypt KDF: Incorrect encodings - input is encoded differently to what was configured", function(t) { | ||
try { | ||
var kdf = new scrypt.KDF(); | ||
kdf.config.keyEncoding = "base64"; | ||
kdf('error', scryptParameters); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the input encoding is ascii and that differs to the requested config of base64"); | ||
t.deepEqual(err, scrypt.errorObject(ADDONARG,"key is probably encoded differently to what was specified"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
} | ||
it("Will correctly verify hash as false if different keys are used for kdf and verify", function(){ | ||
var key = "this is a key" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}) | ||
, result = scrypt.verifyKdfSync(kdf, new Buffer("Another key")); | ||
try { | ||
kdf("error", scryptParameters, function(err, result){}); | ||
} catch (err) { | ||
t.ok(err, "Synchronous test - An error was correctly thrown because the input encoding is ascii and that differs to the requested config of base64"); | ||
t.deepEqual(err,scrypt.errorObject(ADDONARG,"key is probably encoded differently to what was specified"), "The correct object is returned, namely: " + JSON.stringify(err)); | ||
t.end(); | ||
} | ||
}); | ||
expect(result) | ||
.to.be.a("boolean") | ||
.to.equal(false); | ||
}); | ||
}); | ||
describe("Asynchronous", function() { | ||
it("Will use random salt to ensure no two KDFs are the same, even if the keys are identical", function(done) { | ||
scrypt.kdf("password", {N:1, r:1, p:1}, function(err, result1) { | ||
expect(err) | ||
.to.not.exist; | ||
scrypt.kdf("password", {N:1, r:1, p:1}, function(err, result2) { | ||
expect(err) | ||
.to.not.exist; | ||
expect(result1.toString("base64")) | ||
.to.not.equal(result2.toString("base64")); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
it("Will correctly verify hash as true if identical keys are used for kdf and verify", function(done){ | ||
var key = "this is a key" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}); | ||
scrypt.verifyKdf(kdf, key, function(err, result) { | ||
expect(result) | ||
.to.be.a("boolean") | ||
.to.equal(true); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
it("Will correctly verify hash as false if different keys are used for kdf and verify", function(done){ | ||
var key = "this is a key" | ||
, kdf = scrypt.kdfSync(key, {N:1, r:1, p:1}); | ||
scrypt.verifyKdf(kdf, "Another Key", function(err, result) { | ||
expect(result) | ||
.to.be.a("boolean") | ||
.to.equal(false); | ||
expect(err) | ||
.to.not.exist; | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe("Hash Logic", function() { | ||
var hash_length = Math.floor(Math.random() * 100) + 1; //Choose random number between 1 and 100 | ||
describe("Synchronous", function() { | ||
it("Will be deterministic if salts are identical", function() { | ||
var result1 = scrypt.hashSync(new Buffer("hash something"), {N:1, r:1, p:1}, hash_length, "NaCl"); | ||
expect(result1) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result1) | ||
.to.have.length(hash_length); | ||
var result2 = scrypt.hashSync("hash something", {N:1, r:1, p:1}, hash_length, new Buffer("NaCl")); | ||
expect(result2) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result2) | ||
.to.have.length(hash_length); | ||
expect(result1.toString("base64")) | ||
.to.equal(result2.toString("base64")); | ||
}); | ||
}); | ||
describe("Asynchronous", function() { | ||
it("Will be deterministic if salts are identical", function(done) { | ||
scrypt.hash(new Buffer("hash something"), {N:1, r:1, p:1}, hash_length, "NaCl", function(err, result1) { | ||
expect(result1) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result1) | ||
.to.have.length(hash_length); | ||
expect(err) | ||
.to.not.exist; | ||
scrypt.hash("hash something", {N:1, r:1, p:1}, hash_length, new Buffer("NaCl"), function(err, result2) { | ||
expect(result2) | ||
.to.be.an.instanceof(Buffer); | ||
expect(result2) | ||
.to.have.length(hash_length); | ||
expect(err) | ||
.to.not.exist; | ||
expect(result1.toString("base64")) | ||
.to.equal(result2.toString("base64")); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
70
988
2106206
3
408
4
+ Addednan@2.22.0(transitive)
- Removednan@1.9.0(transitive)
Updatednan@^2.0.8