scrypt-js vs scryptsy
JavaScript Scrypt Libraries Comparison
1 Year
scrypt-jsscryptsy
What's JavaScript Scrypt Libraries?

Both scrypt-js and scryptsy are JavaScript implementations of the scrypt key derivation function, which is designed to be computationally intensive to resist brute-force attacks. These libraries are used for securely hashing passwords and generating keys, making them essential for applications that require strong security measures against unauthorized access. While both libraries serve similar purposes, they differ in performance, compatibility, and ease of use, catering to different developer needs and environments.

Package Weekly Downloads Trend
Github Stars Ranking
Stat Detail
Package
Downloads
Stars
Size
Issues
Publish
License
scrypt-js1,164,864142-125 years agoMIT
scryptsy93,79847-66 years agoMIT
Feature Comparison: scrypt-js vs scryptsy

Performance

  • scrypt-js:

    scrypt-js is a pure JavaScript implementation, which means it may not be as fast as native implementations. It is suitable for environments where performance is not the primary concern, but it may struggle with high-load scenarios or large datasets due to its JavaScript execution speed.

  • scryptsy:

    scryptsy offers superior performance due to its native C++ bindings. This allows it to execute hashing operations much faster than pure JavaScript implementations, making it ideal for applications that require rapid password hashing or need to process many hashes in a short time.

Environment Compatibility

  • scrypt-js:

    scrypt-js is designed to work seamlessly in both Node.js and browser environments. This makes it a versatile choice for developers looking to implement scrypt hashing in client-side applications or in server-side JavaScript environments without worrying about compatibility issues.

  • scryptsy:

    scryptsy is primarily designed for Node.js environments due to its reliance on native bindings. While it can be used in some browser contexts through bundling, it is not as straightforward as scrypt-js, which may limit its use in client-side applications.

Ease of Use

  • scrypt-js:

    scrypt-js has a straightforward API that is easy to integrate into applications. Developers can quickly get started with minimal setup, making it a good choice for those who prioritize simplicity and quick implementation.

  • scryptsy:

    scryptsy, while also providing a clear API, may require additional setup due to its native bindings. Developers need to ensure that the native module is correctly installed and configured, which can add complexity for those unfamiliar with native module handling in Node.js.

Security Features

  • scrypt-js:

    scrypt-js implements the scrypt algorithm effectively, providing strong security for password hashing. However, being a pure JavaScript implementation, it may be more susceptible to certain types of attacks if not used in conjunction with other security measures, such as salting and stretching.

  • scryptsy:

    scryptsy leverages the same strong scrypt algorithm but benefits from the performance and efficiency of native execution. This can enhance security by allowing for more iterations and a higher cost factor without significantly impacting performance, making it harder for attackers to brute-force hashed passwords.

Community and Support

  • scrypt-js:

    scrypt-js has a decent level of community support due to its simplicity and ease of use. However, being a pure JavaScript library, it may not have as extensive a user base as some other libraries, which can affect the availability of resources and examples.

  • scryptsy:

    scryptsy, with its performance advantages, has garnered attention among developers focused on security and performance. It may have a more niche community, but the focus on performance and security can lead to more specialized support and resources.

How to Choose: scrypt-js vs scryptsy
  • scrypt-js:

    Choose scrypt-js if you need a pure JavaScript implementation that is compatible with both Node.js and browser environments. It is suitable for applications where portability is a priority and where you may not have control over the execution environment.

  • scryptsy:

    Choose scryptsy if performance is critical for your application. It is built on native C++ bindings, which can significantly speed up the hashing process compared to pure JavaScript implementations. This is particularly important for applications that require high throughput or need to handle large volumes of password hashing.

README for scrypt-js

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