Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS encryption: re-use cipher object to improve performance

I would like to nodejs and an encrypted MongoDB database. I am concerned about performance. Consider the following use case:

  1. I have an encrypted DB from which I retrieve a list of encrypted strings (names for example) [_encrypted_name_1, _encrypted_name_2, ...]
  2. I would like to decrypt all elements from that list

Since I am concerned about performance I did some tests to figure it out. I observed that encrypting/decrypting lots of small strings is very slow compared to encrypting/decrypting a very large string.

Consider the following example:

var crypto = require('crypto'),
    _ = require('lodash'),
    encryptedStringArray = [],
    decryptedStringArray = [],
    encryptedLongString,
    NB_ITERATION = 100000,
    stringArray = [],
    longString = '',
    myString = 'Your Name';

function encrypt(text){
    var cipher = crypto.createCipher('aes-256-cbc', 'd6F3Efeq');
    var crypted = cipher.update(text, 'utf8', 'hex');
    crypted += cipher.final('hex');
    return crypted;
}
function decrypt(text){
    var decipher = crypto.createDecipher('aes-256-cbc', 'd6F3Efeq');
    var dec = decipher.update(text, 'hex', 'utf8');
    dec += decipher.final('utf8');
    return dec;
}

// SLOW: ARRAY OF STRINGS
console.time("slow");
for (var i = 0; i < NB_ITERATION; i += 1) {
    stringArray.push(myString);
}

_.forEach(stringArray, function (item) {
    encryptedStringArray.push(encrypt(item));
});

_.forEach(encryptedStringArray, function (item) {
    decryptedStringArray.push(decrypt(item)); //.toString());
});
console.timeEnd("slow");


// FAST: SUPER LONG STRING
console.time("fast");
for (var i = 0; i < NB_ITERATION; i += 1) {
    longString += myString;
}
encryptedLongString = encrypt(longString);

decrypt(encryptedLongString);
console.timeEnd("fast");


// **********************************************************************
//  FOR LOOP
// **********************************************************************
//
console.time("for_loop");
stringArray = [];
encryptedStringArray = [];
decryptedStringArray = [];
for (var i = 0; i < NB_ITERATION; i += 1) {
    stringArray.push(myString);
}

_.forEach(stringArray, function (item) {
    encryptedStringArray.push(myString);
});

_.forEach(encryptedStringArray, function (item) {
    decryptedStringArray.push(myString);
});
console.timeEnd("for_loop");



// **********************************************************************
//  CREATION OF CIPHER ONLY - NO ENCRYPTION
// **********************************************************************
function noencrypt(text){
    var cipher = crypto.createCipher('aes-256-cbc', 'd6F3Efeq');
    // var crypted = cipher.update(text, 'utf8', 'hex');
    // crypted += cipher.final('hex');
    // return crypted;
    return text;
}
function nodecrypt(text){
    var decipher = crypto.createDecipher('aes-256-cbc', 'd6F3Efeq');
    // var dec = decipher.update(text, 'hex', 'utf8');
    // dec += decipher.final('utf8');
    // return dec;
    return text;
}

// SLOW
console.time("slow_nocrypt");
for (var i = 0; i < NB_ITERATION; i += 1) {
    stringArray.push(myString);
}

_.forEach(stringArray, function (item) {
    encryptedStringArray.push(noencrypt(item));
});

_.forEach(encryptedStringArray, function (item) {
    decryptedStringArray.push(nodecrypt(item)); //.toString());
});
console.timeEnd("slow_nocrypt");


// FAST
console.time("fast_nocrypt");
for (var i = 0; i < NB_ITERATION; i += 1) {
    longString += myString;
}
encryptedLongString = noencrypt(longString);

nodecrypt(encryptedLongString);
console.timeEnd("fast_nocrypt");

Here are the results:

  • slow: 2078ms
  • fast: 20ms
  • for_loop: 14ms
  • slow_nocrypt: 1898ms
  • fast_nocrypt: 1ms

Most of the time is spent creating Cipher objects. Therefore, I would like to use the same cipher object to encrypt/decrypt a list of of strings. In this case one needs to properly deal with the initialisation vector:

  • How to deal with the initialisation vector?
  • Once a cipher object is created, is it possible to change its initialisation vector?

The ideal scenario would probably be to use stream objects illustrated by the following pseudo-code:

var myArray = [
    {to_encrypt: 'Your Name 1', iv: INIT_VECTOR_1}, 
    {to_encrypt: 'Your Name 2', iv: INIT_VECTOR_2}];
var encrypted_array = [];

streamify(myArray)
    .pipe(CIPHER_WITH_IV_UPDATE)
    .write(streamify(encrypted_array));
like image 717
apairet Avatar asked Apr 16 '26 02:04

apairet


1 Answers

Your code is actually slow because symmetric algorithms work in discrete blocks.

When you encrypt the single string Your Name, the cipher will pad it with random bytes to reach a multiple of the block size (128 bits).

Therefore, your slow version is actually encrypting more data per string.

To speed it up, either use a smaller block size or encrypt more data per block.

like image 137
SLaks Avatar answered Apr 18 '26 15:04

SLaks



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!