Giter Club home page Giter Club logo

scrypt-js's Introduction

scrypt

The scrypt password-base key derivation function (pbkdf) is an algorithm designed to be brute-force resistant that converts human readable passwords into fixed length arrays of bytes, which can then be used as a key for symmetric block ciphers, private keys, et cetera.

Features:

  • Non-blocking - Gives other events in the event loop opportunities to run (asynchronous)
  • Cancellable - If the key is no longer required, the computation can be cancelled
  • Progress Callback - Provides the current progress of key derivation as a percentage complete

Tuning

The scrypt algorithm is, by design, expensive to execute, which increases the amount of time an attacker requires in order to brute force guess a password, adjustable by several parameters which can be tuned:

  • N - The CPU/memory cost; increasing this increases the overall difficulty
  • r - The block size; increasing this increases the dependency on memory latency and bandwidth
  • p - The parallelization cost; increasing this increases the dependency on multi-processing

Installing

node.js

If you do not require the progress callback or cancellable features, and your application is specific to node.js, you should likely use the built-in crypto package.

Otherwise, to install in node.js, use:

npm install scrypt-js

browser

<script src="https://raw.githubusercontent.com/ricmoo/scrypt-js/master/scrypt.js" type="text/javascript"></script>

API

scrypt . scrypt ( password , salt , N , r , p , dkLen [ , progressCallback ] ) => Promise

Compute the scrypt PBKDF asynchronously using a Promise. If progressCallback is provided, it is periodically called with a single parameter, a number between 0 and 1 (inclusive) indicating the completion progress; it will always emit 0 at the beginning and 1 at the end, and numbers between may repeat.

scrypt . syncScrypt ( password , salt , N , r , p , dkLen ) => Uint8Array

Compute the scrypt PBKDF synchronously. Keep in mind this may stall UI and other tasks and the asynchronous version is highly preferred.

Example

<html>
  <body>
    <div><span id="progress"></span>% complete...</div>
    <!-- These two libraries are highly recommended for encoding password/salt -->
    <script src="libs/buffer.js" type="text/javascript"></script>

    <!-- This shim library greatly improves performance of the scrypt algorithm -->
    <script src="libs/setImmediate.js" type="text/javascript"></script>

    <script src="index.js" type="text/javascript"></script>
    <script type="text/javascript">

      // See the section below: "Encoding Notes"
      const password = new buffer.SlowBuffer("anyPassword".normalize('NFKC'));
      const salt = new buffer.SlowBuffer("someSalt".normalize('NFKC'));

      const N = 1024, r = 8, p = 1;
      const dkLen = 32;

      function updateInterface(progress) {
          document.getElementById("progress").textContent = Math.trunc(100 * progress);
      }

      // Async
      const keyPromise = scrypt.scrypt(password, salt, N, r, p, dkLen, updateInterface);

      keyPromise.then(function(key) {
          console.log("Derived Key (async): ", key);
      });

      // Sync
      const key = scrypt.syncScrypt(password, salt, N, r, p, dkLen);
      console.log("Derived Key (sync): ", key);
    </script>
  </body>
</html>

Encoding Notes

TL;DR - either only allow ASCII characters in passwords, or use
        String.prototype.normalize('NFKC') on any password

It is HIGHLY recommended that you do NOT pass strings into this (or any password-base key derivation function) library without careful consideration; you should convert your strings to a canonical format that you will use consistently across all platforms.

When encoding passwords with UTF-8, it is important to realize that there may be multiple UTF-8 representations of a given string. Since the key generated by a password-base key derivation function is dependent on the specific bytes, this matters a great deal.

Composed vs. Decomposed

Certain UTF-8 code points can be combined with other characters to create composed characters. For example, the letter a with the umlaut diacritic mark (two dots over it) can be expressed two ways; as its composed form, U+00FC; or its decomposed form, which is the letter "u" followed by U+0308 (which basically means modify the previous character by adding an umlaut to it).

// In the following two cases, a "u" with an umlaut would be seen
> '\u00fc'
> 'u\u0308'


// In its composed form, it is 2 bytes long
> new Buffer('u\u0308'.normalize('NFKC'))
<Buffer c3 bc>
> new Buffer('\u00fc')
<Buffer c3 bc>

// Whereas the decomposed form is 3 bytes, the letter u followed by U+0308
> new Buffer('\u00fc'.normalize('NFKD'))
<Buffer 75 cc 88>
> new Buffer('u\u0308')
<Buffer 75 cc 88>

Compatibility equivalence mode

Certain strings are often displayed the same, even though they may have different semantic means. For example, UTF-8 provides a code point for the roman number for one, which appears as the letter I, in most fonts identically. Compatibility equivalence will fold these two cases into simply the capital letter I.

> '\u2160'
'I'
> 'I'
'I'
> '\u2160' === 'I'
false
> '\u2160'.normalize('NFKC') === 'I'
true

Normalizing

The normalize() method of a string can be used to convert a string to a specific form. Without going into too much detail, I generally recommend NFKC, however if you wish to dive deeper into this, a nice short summary can be found in Pythons unicodedata module's documentation.

For browsers without normalize() support, the npm unorm module can be used to polyfill strings.

Another example of encoding woes

One quick story I will share is a project which used the SHA256(encodeURI(password)) as a key, which (ignoring rainbow table attacks) had an unfortunate consequence of old web browsers replacing spaces with + while on new web browsers, replacing it with a %20, causing issues for anyone who used spaces in their password.

Suggestions

  • While it may be inconvenient to many international users, one option is to restrict passwords to a safe subset of ASCII, for example: /^[A-Za-z0-9!@#$%^&*()]+$/.
  • My personal recommendation is to normalize to the NFKC form, however, one could imagine setting their password to a Chinese phrase on one computer, and then one day using a computer that does not have Chinese input capabilities and therefore be unable to log in.

See: Unicode Equivalence

Tests

The test cases from the scrypt whitepaper are included in test/test-vectors.json and can be run using:

npm test

Special Thanks

I would like to thank @dchest for his scrypt-async library and for his assistance providing feedback and optimization suggestions.

License

MIT license.

References

Donations

Obviously, it's all licensed under the MIT license, so use it as you wish; but if you'd like to buy me a coffee, I won't complain. =)

  • Ethereum - ricmoo.eth

scrypt-js's People

Contributors

kolahzary avatar nabijaczleweli avatar ricmoo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

scrypt-js's Issues

Evaluate usage of the WebCrypto API in order to speed up the computation

It would be interesting to evaluate usage of the WebCrypto API in order to speed up the computation.

Here is the possible cross compatible way of using the SHA implemented using web workers: https://github.com/globaleaks/GlobaLeaks/blob/master/client/app/js/crypto/main.js#L48

the example takes into account the difference of API implemented by the great and inimitable Internet Explorer.

This would make the library more secure, modern and efficient.

Providing a sync version

Hi @ricmoo,

I'm opening this issue because I'm interested in using this library instead of scryptsy, as that one uses crypto-browserify in the browser, and that implies a huge bundle that normally doesn't get tree-shaken and doesn't seem to work with Rollup.

The problem is that some projects, like ethereumjs-wallet here, use the sync version of scryptsy. This is not ideal, but can't be changed without introducing a breaking change.

I'm wondering if you are open to adding a sync version to this library.

Thanks!

Demo doesn't accept hex salts longer than 2 nibbles

On the demo page, entering any reasonable salt and selecting hex will return error: "salt: invalid hex string"

The code:

 } else if (form.indexOf('hex') >= 0) {
                    if (!value.match(/^[0-9A-F][0-9A-F]$/i)) {
                        throw new Error(field + ': invalid hex string');
                    }
                    return new buffer.SlowBuffer(value, 'hex');
                }

Add/Update Github Security Page/Policy

Since this is a crypto library, I think it would be prudent to have fleshed out github security page.
And it's one of the things that sort of has to come from the library owner.

[Bug] Error when input password more than 64byte

How to reproduce:

  1. Open the page: http://ricmoo.github.io/scrypt-js/
  2. Input password more than 64 bytes.
  3. Click "Compute scrypt"

This make a error:

scrypt.js:90 Uncaught TypeError: p.push is not a function
    at SHA256 (scrypt.js:90)
    at PBKDF2_HMAC_SHA256_OneIter (scrypt.js:117)
    at scrypt (scrypt.js:285)
    at HTMLInputElement.submit.onclick ((index):254)

scrypt function passes the password to PBKDF2_HMAC_SHA256_OneIter function,
When password is more than 64bytes, PBKDF2_HMAC_SHA256_OneIter function run SHA256 for password.
SHA256 expects that the argument is Array but user uses Buffer.

I think PBKDF2_HMAC_SHA256_OneIter should convert password to Array or scrypt should only support Array.

Hash the salt?

Thanks for your works on this!

I want to leave up the password and salt(could be username, email, phone, ...) for the user and save the key and the key of the salt like below is it a good practice?

const N = 65536, r = 8, p = 1;
const dkLen = 32;

function hash(password, salty) {
	const passwordBuffer = new buffer.SlowBuffer(password.normalize('NFKC'));
	const saltBuffer = new buffer.SlowBuffer(salty.normalize('NFKC'));
	const saltKey = scrypt.syncScrypt(saltBuffer, saltBuffer, N, r, p, dkLen);

	const key = scrypt.syncScrypt(passwordBuffer, saltKey, N, r, p, dkLen);
	const hexKey = Buffer.from(key).toString('hex');
	const hexSalt = Buffer.from(saltKey).toString('hex');

	return hexKey+":"+hexSalt;
}

Integrate usage of a browser-testing library in order to track cross browser compatibility

It would be great if the library could Integrate usage of a browser-testing library in order to track cross browser compatibility.

A good choiche could be Saucelabs that is free for an opensource project like your: https://saucelabs.com/open-source

This way we could avaluate the integration of library inside GlobaLeaks.

Examples on how to do this could be found in: https://github.com/dchest/scrypt-async-js

README issues or questions

1. scrypt in node's "crypto"

The README still points to node-scrypt - last updated 4 years ago (related issue: #18).

However, it seems node.js's own crypto module supports scrypt so maybe the link should just say to use that implementation?

https://nodejs.org/api/crypto.html#crypto_crypto_scrypt_password_salt_keylen_options_callback

2. Code example reference to "buffer.js"

EDIT invalid, for some reason I overlooked the "thirdparty" folder. Text removed.

3. Why Buffer?

Why not just use a TypedArray? I'm missing an explanation for why I'm told to use the node.js Buffer emulation.

That soft dependency seems like significant overhead overhead, it's far more than the scrypt package itself!

4. Return type

Document the return type.

On node.js I get a Buffer. Here it seems I get an Array (I only looked at the source? Of what exactly? Without TypeScript-like types it's really hard to see what I get - maybe add a ".d.ts" file with that information as TypeScript types? The example code just uses console.log, which because of implicit JS type conversions toString() does not tell me anything useful. Normally I would expect a Buffer, no?

[Feature Request] Add Optional? WebAssembly Support

As far as I can tell, multithreading support for WASM is coming and could provide comparable performance to the Node.js libraries.
Since this is the only library I could find that had browser support, I think it would be ideally suited to provide both a WASM solution and pure js fallbacks to maintain cross compatibility between Node.js and Browsers.

I've seen a few repos that seem to have attempted this, but they are all either not finished or not actively maintained/have no usage.

An additional benefit might be adding the SCrypt source/core C code as a dependency for tracking any issues with the function itself.

I might try building/making a pull request if it's something others agree might belong in this repo/library.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.