Code Monkey home page Code Monkey logo

crypto-async's Introduction

@ronomon/crypto-async

Fast, reliable cipher, hash and hmac methods executed in Node's threadpool for multi-core throughput.

Motivation

Some longstanding issues with Node's crypto module

  • Did you know that Node's cipher, hash and hmac streams are not truly asynchronous? They execute in C, but only in the main thread and so the crypto module blocks your event loop. Encrypting 64 MB of data might block your event loop for +/- 70ms. Hashing 64 MB of data might block your event loop for +/- 190ms. This will spike any concurrent user-visible request latencies.
  • Worse, the crypto module does not take advantage of multiple CPU cores. Your server may have four CPU cores but crypto will use only one of these four CPU cores for all encrypting and hashing operations. The cluster module with its IPC overhead is not an efficient solution to multi-core crypto.
  • The crypto module was sadly not designed to use statically allocated buffers, allocating a new output buffer when encrypting or hashing data, even if you already have an output buffer available. If you want to hash only a portion of a buffer you must first create a slice. Creating thousands of Javascript objects in this way strains the GC, leads to longer GC pauses and further blocks your event loop.
  • The crypto module forces multiple unnecessary roundtrips between JS and C even if you are only encrypting or hashing a single buffer. When your buffer is small (less than a few hundred bytes), this calling overhead alone, of a few hundred nanoseconds per call, can double your latencies and halve your throughput.
  • In summary, the crypto module is not suitable for high-throughput network protocols or storage systems, which need to checksum and encrypt/decrypt huge amounts of data concurrently. Such a user-space network protocol or storage system using the crypto module might saturate a single CPU core with crypto operations well before saturating a fast local network or SSD disk.

Some new ideas for @ronomon/crypto-async

  • Truly asynchronous. All operations can execute asynchronously in Node's threadpool. This keeps your event loop free from blocking.
  • Scales across multiple CPU cores. While @ronomon/crypto-async is a fraction slower per call than crypto because of the overhead of pushing tasks into the threadpool, for buffers larger than 1024 bytes it shines and provides nearly N-cores more throughput. Don't let your CPU cores go to waste.
  • Zero-copy. All keys, ivs, source and target arguments can be passed directly using offsets into existing buffers, without requiring any slices and without allocating any temporary output buffers. This enables predictable memory usage for programs with tight memory budgets.
  • Fast. Supports the common use case of encrypting or hashing a single buffer, to avoid multiple round-trips between JS and C. This halves latencies and doubles throughput for small buffers.
  • Synchronous where it makes sense. While you should use asynchronous methods for large buffers to improve throughput, you can also use synchronous methods for small buffers to achieve optimal latency.

Performance


                CPU: Intel(R) Xeon(R) CPU E3-1230 V2 @ 3.30GHz
              Cores: 8
            Threads: 4

========================================================================

        aes-256-ctr: 16384 x 256 Bytes
               node: Latency: 0.008ms Throughput: 29.09 MB/s
      sync @ronomon: Latency: 0.003ms Throughput: 76.70 MB/s
     async @ronomon: Latency: 0.047ms Throughput: 21.04 MB/s

        aes-256-ctr: 16384 x 1024 Bytes
               node: Latency: 0.007ms Throughput: 132.43 MB/s
      sync @ronomon: Latency: 0.003ms Throughput: 340.46 MB/s
     async @ronomon: Latency: 0.045ms Throughput: 88.86 MB/s

        aes-256-ctr: 16384 x 4096 Bytes
               node: Latency: 0.009ms Throughput: 439.00 MB/s
      sync @ronomon: Latency: 0.004ms Throughput: 1010.11 MB/s
     async @ronomon: Latency: 0.043ms Throughput: 376.36 MB/s

        aes-256-ctr: 1024 x 65536 Bytes
               node: Latency: 0.046ms Throughput: 1402.22 MB/s
      sync @ronomon: Latency: 0.030ms Throughput: 2154.93 MB/s
     async @ronomon: Latency: 0.088ms Throughput: 2938.77 MB/s

        aes-256-ctr: 64 x 1048576 Bytes
               node: Latency: 0.717ms Throughput: 1460.21 MB/s
      sync @ronomon: Latency: 0.452ms Throughput: 2314.90 MB/s
     async @ronomon: Latency: 1.372ms Throughput: 3013.60 MB/s

========================================================================

        aes-256-gcm: 16384 x 256 Bytes
               node: Latency: 0.009ms Throughput: 27.99 MB/s
      sync @ronomon: Latency: 0.003ms Throughput: 82.62 MB/s
     async @ronomon: Latency: 0.042ms Throughput: 24.11 MB/s

        aes-256-gcm: 16384 x 1024 Bytes
               node: Latency: 0.009ms Throughput: 105.41 MB/s
      sync @ronomon: Latency: 0.004ms Throughput: 253.50 MB/s
     async @ronomon: Latency: 0.042ms Throughput: 94.61 MB/s

        aes-256-gcm: 16384 x 4096 Bytes
               node: Latency: 0.013ms Throughput: 314.20 MB/s
      sync @ronomon: Latency: 0.006ms Throughput: 621.70 MB/s
     async @ronomon: Latency: 0.043ms Throughput: 375.18 MB/s

        aes-256-gcm: 1024 x 65536 Bytes
               node: Latency: 0.091ms Throughput: 719.20 MB/s
      sync @ronomon: Latency: 0.061ms Throughput: 1065.52 MB/s
     async @ronomon: Latency: 0.113ms Throughput: 2285.47 MB/s

        aes-256-gcm: 64 x 1048576 Bytes
               node: Latency: 1.063ms Throughput: 986.12 MB/s
      sync @ronomon: Latency: 0.944ms Throughput: 1109.59 MB/s
     async @ronomon: Latency: 1.516ms Throughput: 2715.93 MB/s

========================================================================

             sha256: 16384 x 256 Bytes
               node: Latency: 0.007ms Throughput: 36.79 MB/s
      sync @ronomon: Latency: 0.002ms Throughput: 101.47 MB/s
     async @ronomon: Latency: 0.042ms Throughput: 24.05 MB/s

             sha256: 16384 x 1024 Bytes
               node: Latency: 0.008ms Throughput: 124.19 MB/s
      sync @ronomon: Latency: 0.004ms Throughput: 224.30 MB/s
     async @ronomon: Latency: 0.043ms Throughput: 92.59 MB/s

             sha256: 16384 x 4096 Bytes
               node: Latency: 0.016ms Throughput: 240.94 MB/s
      sync @ronomon: Latency: 0.013ms Throughput: 319.26 MB/s
     async @ronomon: Latency: 0.040ms Throughput: 398.04 MB/s

             sha256: 2048 x 65536 Bytes
               node: Latency: 0.201ms Throughput: 325.30 MB/s
      sync @ronomon: Latency: 0.188ms Throughput: 349.06 MB/s
     async @ronomon: Latency: 0.273ms Throughput: 955.41 MB/s

             sha256: 128 x 1048576 Bytes
               node: Latency: 3.013ms Throughput: 347.94 MB/s
      sync @ronomon: Latency: 3.003ms Throughput: 349.09 MB/s
     async @ronomon: Latency: 3.310ms Throughput: 1257.44 MB/s

========================================================================

        hmac-sha256: 16384 x 256 Bytes
               node: Latency: 0.009ms Throughput: 27.94 MB/s
      sync @ronomon: Latency: 0.003ms Throughput: 69.70 MB/s
     async @ronomon: Latency: 0.038ms Throughput: 26.30 MB/s

        hmac-sha256: 16384 x 1024 Bytes
               node: Latency: 0.010ms Throughput: 97.52 MB/s
      sync @ronomon: Latency: 0.006ms Throughput: 176.88 MB/s
     async @ronomon: Latency: 0.036ms Throughput: 111.07 MB/s

        hmac-sha256: 16384 x 4096 Bytes
               node: Latency: 0.019ms Throughput: 212.33 MB/s
      sync @ronomon: Latency: 0.014ms Throughput: 285.50 MB/s
     async @ronomon: Latency: 0.039ms Throughput: 411.16 MB/s

        hmac-sha256: 2048 x 65536 Bytes
               node: Latency: 0.198ms Throughput: 330.22 MB/s
      sync @ronomon: Latency: 0.191ms Throughput: 342.88 MB/s
     async @ronomon: Latency: 0.256ms Throughput: 1019.00 MB/s

        hmac-sha256: 128 x 1048576 Bytes
               node: Latency: 3.025ms Throughput: 346.55 MB/s
      sync @ronomon: Latency: 2.926ms Throughput: 358.31 MB/s
     async @ronomon: Latency: 3.214ms Throughput: 1298.56 MB/s

Installation

This will install @ronomon/crypto-async and compile the native binding automatically:

npm install @ronomon/crypto-async

Usage

Adjust threadpool size and control concurrency

Node runs filesystem and DNS operations in the threadpool. The threadpool consists of 4 threads by default, which is far from optimal. This means that at most 4 operations can be running at any point in time. If any operation is slow to complete, it will cause head-of-line blocking, otherwise known as the Convoy effect.

The size of the threadpool should therefore be increased at startup time (at the top of your script, before requiring any modules) by setting the UV_THREADPOOL_SIZE environment variable. The absolute maximum is 128 threads, which requires only ~1 MB memory in total according to the libuv docs.

Again, conventional wisdom would set the number of threads to the number of CPU cores, but most operations running in the threadpool are not run hot, they are not CPU-intensive and block mostly on IO. Issuing more IO operations than there are CPU cores will increase throughput and will decrease latency per operation by decreasing queueing time. On the other hand, @ronomon/crypto-async is CPU-intensive. Issuing more @ronomon/crypto-async operations than there are CPU cores will not increase throughput and will increase latency per operation by increasing queueing time.

You should therefore:

  1. Set the threadpool size to IO + N, where IO is the number of filesystem and DNS operations you expect to be running concurrently, and where N is the number of CPU cores available. This will reduce head-of-line blocking.

  2. Allow or design for at most N @ronomon/crypto-async operations to be running concurrently, where N is the number of CPU cores available. This will keep latency within reasonable bounds.

// At the top of your script, before requiring any modules:
process.env['UV_THREADPOOL_SIZE'] = 128;

Synchronous method alternatives

All methods have a synchronous method alternative: just leave out the callback when calling the method. These are convenient for small buffers and outperform the crypto module equivalents.

Cipher whitelist

@ronomon/crypto-async disables slow, complicated ciphers such as CCM and dangerous ciphers such as CBC and ECB. A limited whitelist of stream ciphers and AEAD ciphers are supported. This is a good thing in the interest of a safe implementation.

Supported stream ciphers

These are dangerous if you do not encrypt-then-mac:

  • chacha20 (keySize=32, ivSize=16)
  • aes-256-ctr (keySize=32, ivSize=16)
  • aes-192-ctr (keySize=24, ivSize=16)
  • aes-128-ctr (keySize=16, ivSize=16)
Supported AEAD ciphers

These are recommended over stream ciphers for safety, ease-of-use and efficiency:

  • chacha20-poly1305 (keySize=32, ivSize=12, tagSize=16)
  • aes-256-gcm (keySize=32, ivSize=12, tagSize=16)
  • aes-128-gcm (keySize=16, ivSize=12, tagSize=16)

Cipher

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'aes-256-ctr';
var encrypt = 1; // Encrypt
var key = Buffer.alloc(32);
var iv = Buffer.alloc(16);
var plaintext = Buffer.alloc(128);
cryptoAsync.cipher(algorithm, encrypt, key, iv, plaintext,
  function(error, ciphertext) {
    if (error) throw error;
    console.log('ciphertext:', ciphertext.toString('hex'));
    var encrypt = 0; // Decrypt
    cryptoAsync.cipher(algorithm, encrypt, key, iv, ciphertext,
      function(error, plaintext) {
        if (error) throw error;
        console.log('plaintext:', plaintext.toString('hex'));
      }
    );
  }
);

Cipher (AEAD)

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'chacha20-poly1305';
var encrypt = 1; // Encrypt
var key = Buffer.alloc(32);
var iv = Buffer.alloc(12);
var plaintext = Buffer.alloc(128);
var aad = Buffer.alloc(256);
var tag = Buffer.alloc(16);
cryptoAsync.cipher(algorithm, encrypt, key, iv, plaintext, aad, tag,
  function(error, ciphertext) {
    if (error) throw error;
    console.log('ciphertext:', ciphertext.toString('hex'));
    console.log('tag:', tag.toString('hex'));
    var encrypt = 0; // Decrypt
    cryptoAsync.cipher(algorithm, encrypt, key, iv, ciphertext, aad, tag,
      function(error, plaintext) {
        if (error) {
          if (error.message === cryptoAsync.E_CORRUPT) {
            throw new Error('key/iv/source/aad/tag failed authentication');
          } else {
            throw error;
          }
        }
        console.log('plaintext:', plaintext.toString('hex'));
      }
    );
  }
);

Hash

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'sha256';
var source = Buffer.alloc(1024 * 1024);
cryptoAsync.hash(algorithm, source,
  function(error, hash) {
    if (error) throw error;
    console.log('hash:', hash.toString('hex'));
  }
);

HMAC

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'sha256';
var key = Buffer.alloc(1024);
var source = Buffer.alloc(1024 * 1024);
cryptoAsync.hmac(algorithm, key, source,
  function(error, hmac) {
    if (error) throw error;
    console.log('hmac:', hmac.toString('hex'));
  }
);

Zero-Copy Methods

These methods require more arguments but support zero-copy crypto operations for reduced memory overhead and GC pressure.

Cipher (Zero-Copy)

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'aes-256-ctr';
var encrypt = 1; // Encrypt
var key = Buffer.alloc(1024);
var keyOffset = 4;
var keySize = 32;
var iv = Buffer.alloc(32);
var ivOffset = 2;
var ivSize = 16;
var source = Buffer.alloc(1024 * 1024);
var sourceOffset = 512;
var sourceSize = 32;
var target = Buffer.alloc(sourceSize + cryptoAsync.CIPHER_BLOCK_MAX);
var targetOffset = 0;
cryptoAsync.cipher(
  algorithm,
  encrypt,
  key,
  keyOffset,
  keySize,
  iv,
  ivOffset,
  ivSize,
  source,
  sourceOffset,
  sourceSize,
  target,
  targetOffset,
  function(error, targetSize) {
    if (error) throw error;
    var slice = target.slice(targetOffset, targetOffset + targetSize);
    console.log('ciphertext:', slice.toString('hex'));
  }
);

Cipher (Zero-Copy, AEAD)

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'chacha20-poly1305';
var encrypt = 1; // Encrypt
var key = Buffer.alloc(1024);
var keyOffset = 4;
var keySize = 32;
var iv = Buffer.alloc(32);
var ivOffset = 2;
var ivSize = 12;
var source = Buffer.alloc(1024 * 1024);
var sourceOffset = 512;
var sourceSize = 32;
var target = Buffer.alloc(sourceSize + cryptoAsync.CIPHER_BLOCK_MAX);
var targetOffset = 0;
var aad = Buffer.alloc(1024);
var aadOffset = 0;
var aadSize = 10;
var tag = Buffer.alloc(16);
var tagOffset = 0;
var tagSize = 16;
cryptoAsync.cipher(
  algorithm,
  encrypt,
  key,
  keyOffset,
  keySize,
  iv,
  ivOffset,
  ivSize,
  source,
  sourceOffset,
  sourceSize,
  target,
  targetOffset,
  aad,
  aadOffset,
  aadSize,
  tag,
  tagOffset,
  tagSize,
  function(error, targetSize) {
    if (error) {
      if (error.message === cryptoAsync.E_CORRUPT) {
        throw new Error('key/iv/source/aad/tag failed authentication');
      } else {
        throw error;
      }
    }
    var slice = target.slice(targetOffset, targetOffset + targetSize);
    console.log('ciphertext:', slice.toString('hex'));
    console.log('tag:', tag.toString('hex', tagOffset, tagOffset + tagSize));
  }
);

Hash (Zero-Copy)

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'sha256';
var source = Buffer.alloc(1024 * 1024);
var sourceOffset = 512;
var sourceSize = 65536;
var target = Buffer.alloc(1024 * 1024);
var targetOffset = 32768;
cryptoAsync.hash(
  algorithm,
  source,
  sourceOffset,
  sourceSize,
  target,
  targetOffset,
  function(error, targetSize) {
    if (error) throw error;
    var slice = target.slice(targetOffset, targetOffset + targetSize);
    console.log('hash:', slice.toString('hex'));
  }
);

HMAC (Zero-Copy)

var cryptoAsync = require('@ronomon/crypto-async');
var algorithm = 'sha256';
var key = Buffer.alloc(1024);
var keyOffset = 4;
var keySize = 8;
var source = Buffer.alloc(1024 * 1024);
var sourceOffset = 512;
var sourceSize = 65536;
var target = Buffer.alloc(1024 * 1024);
var targetOffset = 32768;
cryptoAsync.hmac(
  algorithm,
  key,
  keyOffset,
  keySize,
  source,
  sourceOffset,
  sourceSize,
  target,
  targetOffset,
  function(error, targetSize) {
    if (error) throw error;
    var slice = target.slice(targetOffset, targetOffset + targetSize);
    console.log('hmac:', slice.toString('hex'));
  }
);

Tests

@ronomon/crypto-async ships with comprehensive fuzz tests, which have uncovered multiple bugs in OpenSSL:

To run the tests:

node test.js

Benchmark

To benchmark @ronomon/crypto-async vs Node's crypto:

node benchmark.js

crypto-async's People

Contributors

jorangreef 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  avatar  avatar

crypto-async's Issues

node-gyp: Error: Could not find any Python installation to use

Steps to reproduce:

  1. Run docker container: docker run -it --entrypoint /bin/bash node:14.18.1-buster-slim
  2. Inside the docker container, run: mkdir test && cd test
  3. Run npm install @ronomon/crypto-async

Ends up on error

@ronomon/[email protected] install /test/node_modules/@ronomon/crypto-async
node-gyp rebuild

gyp ERR! find Python
gyp ERR! find Python Python is not set from command line or npm configuration
gyp ERR! find Python Python is not set from environment variable PYTHON
gyp ERR! find Python checking if "python" can be used
gyp ERR! find Python - "python" is not in PATH or produced an error
gyp ERR! find Python checking if "python2" can be used
gyp ERR! find Python - "python2" is not in PATH or produced an error
gyp ERR! find Python checking if "python3" can be used
gyp ERR! find Python - "python3" is not in PATH or produced an error
gyp ERR! find Python
gyp ERR! find Python **********************************************************
gyp ERR! find Python You need to install the latest version of Python.
gyp ERR! find Python Node-gyp should be able to find and use Python. If not,
gyp ERR! find Python you can try one of the following options:
gyp ERR! find Python - Use the switch --python="/path/to/pythonexecutable"
gyp ERR! find Python (accepted by both node-gyp and npm)
gyp ERR! find Python - Set the environment variable PYTHON
gyp ERR! find Python - Set the npm configuration variable python:
gyp ERR! find Python npm config set python "/path/to/pythonexecutable"
gyp ERR! find Python For more information consult the documentation at:
gyp ERR! find Python https://github.com/nodejs/node-gyp#installation
gyp ERR! find Python **********************************************************
gyp ERR! find Python
gyp ERR! configure error
gyp ERR! stack Error: Could not find any Python installation to use
gyp ERR! stack at PythonFinder.fail (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/find-python.js:307:47)
gyp ERR! stack at PythonFinder.runChecks (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/find-python.js:136:21)
gyp ERR! stack at PythonFinder. (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/find-python.js:179:16)
gyp ERR! stack at PythonFinder.execFileCallback (/usr/local/lib/node_modules/npm/node_modules/node-gyp/lib/find-python.js:271:16)
gyp ERR! stack at exithandler (child_process.js:390:5)
gyp ERR! stack at ChildProcess.errorhandler (child_process.js:402:5)
gyp ERR! stack at ChildProcess.emit (events.js:400:28)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:280:12)
gyp ERR! stack at onErrorNT (internal/child_process.js:469:16)
gyp ERR! stack at processTicksAndRejections (internal/process/task_queues.js:82:21)
gyp ERR! System Linux 5.4.39-linuxkit
gyp ERR! command "/usr/local/bin/node" "/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /test/node_modules/@ronomon/crypto-async
gyp ERR! node -v v14.18.1
gyp ERR! node-gyp -v v5.1.0
gyp ERR! not ok
npm WARN enoent ENOENT: no such file or directory, open '/test/package.json'
npm WARN test No description
npm WARN test No repository field.
npm WARN test No README data
npm WARN test No license field.

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @ronomon/[email protected] install: node-gyp rebuild
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @ronomon/[email protected] install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

Description

The node-gyp package has some dependencies that may not be present on every docker images.

It would be helpful to mention these dependencies in README.

More documentation

Howdy!

Thanks for putting this together! This is awesome.

My question is, is there any way you could create some more documentation about each of the parameters or abstract away some of the details here.

I've got AES CBC working with the following function:

/*
 * Encrypts a plaintext using AES CBC
 * @param {Buffer} key
 * @param {Buffer} iv
 * @param {Buffer} plaintext
 * @returns {String}
 */
let encrypt = function(key, iv, plaintext) {
  let encipher = crypto.createCipheriv('aes-256-cbc', key, iv);
  let ciphertext = encipher.update(plaintext, 'binary', 'binary');
  ciphertext += encipher.final('binary');

  return ciphertext;
};

I'm having trouble understanding how I would switch to crypto-async. Obviously, I would need to use a callback, etc. but there are WAY more params in your function.

In regards to encryption and decryption, could you create a wrapper that just accepted the IV, Key and plaintext and output the ciphertext?

Even if you could just add a basic explanation of each, I'm sure I could figure it out from there. Once I did, I could add a wrapper to this repo as a pull request. I'm just having trouble understanding what each param is for.

Thanks again!

Streaming api

Hi there, would it be possible to add a way of updating a hash or cipher with different calls (like node crypto does) ?

Set engineStrict to true

Just a small thing: Because the engines property in the package.json file is just advisory, npm will install the latest version of crypto-async even on older node.js versions.

Adding "engineStrict": true, to the package file should help with that a bit.

How to easily use this package?

hi @jorangreef how can i very easily to use this package?

when i use node.js native crypto it is very clear:

const crypto = require('crypto');
const hash = crypto.createHash('SHA256');
hash.update('hello world');
console.log('@@',hash.digest('hex'));

how can i get the same result via crypto-async?

Module parse failed: Unexpected character '�' (1:0)

I'm trying to use the lib but I get this error:

ERROR in ./node_modules/@ronomon/crypto-async/binding.node 1:0
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type.
(Source code omitted for this binary file)
 @ ./node_modules/@ronomon/crypto-async/index.js 3:16-41
 @ ./src/utils/EncryptionUtils.js
 @ ./src/actions/InitActions.js
 @ ./src/routes.js
 @ ./src/app.js
 @ ./src/client.js
 @ multi webpack-dev-server/client?http://0.0.0.0:3001 webpack/hot/only-dev-server ./src/client.js

Node version: 10.21.0
OS: Mac OS Catalina (10.15.5)

Binary version

Hi,
Is there a binary version of this package.
I am trying to build version 3.0.1 but node-gyp fails.
In my opinion node gyp add extra complexity to end users that's why I am trying to create a binary version

Add private/public key sign/verify method

Nice repo! I'm hoping it'll solve my JWT/JWS signing performance woes in my package.

Would you happen to have an example on how to sign a JWT with RS256?

return new Promise((resolve, reject) => {
  jws.createSign({
    header: { typ: 'JWT', alg: algorithm },
    privateKey: jwtKey,
    payload: payload,
  })
  .once('done', resolve)
  .once('error', reject)
})

I have this from the popular jws package. Not sure how to recreate that using your crypto instead of node's crypto.

Using with Electron

Electron and crypto-async

I can't get this lib to work with Electron. I made a basic project reproducing my problem here

Bug Description

I can't use the module without recompiling it because, the following code crashes when executed with electron but works perfectly fine when executed on node directly.

const cryptoAsync = require('@ronomon/crypto-async')

try {
  const text = Buffer.from('Hello World')
  const key = Buffer.alloc(16)
  const iv = Buffer.alloc(16)
  console.log('This prints to the console')
  const encryptedText = cryptoAsync.cipher('aes-128-ctr', 0, key, iv, text)
  console.log('But this will not print if the previous call crashed')
  const clearText = cryptoAsync.cipher('aes-128-ctr', 1, key, iv, encryptedText)
  console.log(clearText.toString())
} catch (e) {
  console.error(e)
}

I've tested on both Windows (Windows 10) and Mac OS (El Capitan) and observed two different behaviors (I'm guessing cause MacOS uses the system openssl)

  • Windows
    • It crashes completly with the following exit code
      npm ERR! code ELIFECYCLE
      npm ERR! errno 3221225477
      npm ERR! [email protected] start-electron: `electron .`
      npm ERR! Exit status 3221225477
      
  • Mac OS
    • The first cryptoAsync.cipher method call throws an exception
      Error: algorithm unknown    
      

How to reproduce

  • Clone this repo
  • Run npm i
  • Run npm run start-electron to execute with electron
  • Run npm run start-node to execute with node

Causes

As far as I can tell both errors seems to originate from the same line in binding.c in the @ronomon/crypto-async repo :

const EVP_CIPHER* evp_cipher = EVP_get_cipherbyname(algorithm);
if (!evp_cipher) {
  THROW(env, E_ALGORITHM_UNKNOWN);
}

On windows the EVP_get_cipherbyname method crashes, on Mac it returns null

This seems to be the result of the differences with the bundled version of node in electron.

Rebuilding for Electron

I've also tried to rebuild the native module for electron using the following commands

cd node_modules
cd @ronomon/crypto-async
node-gyp rebuild --target=8.1.0 --arch=x64 --target_platform=win32 --dist-url=https://atom.io/download/atom-shell --module_name=@ronomon/crypto-async --module_path=../lib/binding/electron-v8.1-win32-x64

But the build fails cause openssl/err.h and others openssl headers file cannot be found.

It seems that electron now uses boringssl instead of openssl and does not export the same headers/symbols that Node does.

From the different issues I've come accross on github, I can only think of two solutions (let me know If you can think of any other workaround):

Resource usage (CPU, mem)

Hi.

I have an use case where I'll be hashing small buffers (median should be around 150 bytes, certainly smaller than 512B) on an HTTP endpoint handler where high load is expected. Right now I'm using Node's built-in crypto module and the hashing latency is so far not a problem, but on load testing it was noticed that this hashing generates high CPU usage on the server.

From what I gather from the documentations, this lib focuses mostly on providing higher latency/throughput on crypto operations, but it also mentions having less overhead on calling from JS to C, which leads me to ask, can I also expect crypto-async to have lower CPU usage when compared to built-in crypto?

Thanks in advance

Current support

Hi there,

I was wondering if there's any current support for this library and whether the problems it tries to solve still exist in NodeJS 14.x.

I don't want to introduce and event loop blocker on the API I'm working on, but I don't see any signs of current support and/or security updates for this module and that worries me.

How to use hash function to hash a file stream?

It seems that in current version only a single buffer could be hashed. I want to use it to hash a file stream, like the built-in hash function in crypto module, where update can be called multiple times. Is it possible?

Allow insecure ciphers

Hello.

We're using RC4 from the native crypto module at this moment for non-critical data ciphering. I was excited to upgrade this part of our solution with an async library, but to my surprise I got greeted with a message that told me this algorithm is disabled.

In my opinion, weak ciphers should be allowed as they're even native to Node. Any future plans for allowing this? Perhaps some sort of option? It's a real bummer!

Enable CBC

Hi,
Is there any possibility to decrypt text that was decrypted with AES/CBC/PKCS5Padding?
Your package is the only possibility that we have but CBC is not supported.
In fact it says algorithm disabled.
Best regards

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.