Search code examples
javascriptphpnode.jsencryptionmcrypt

Converting a PHP mcrypt() call to node's mcrypt


I am trying to recreate an encryption function that we have in our old PHP application into a new Node JS application, using Node's mycrypt module.

My goal is the make sure that given the same original string and salt, the PHP script below produces the same encrypted value as the Node script.


PHP

<?php
$string = 'This is my password';
$salt = 'sodiumChloride12';
$encrypted = base64_encode(
    mcrypt_encrypt(
        MCRYPT_RIJNDAEL_128,
        $salt,
        $string,
        MCRYPT_MODE_ECB,
        mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB), MCRYPT_RAND)
    )
);

echo "Encrypted: $encrypted\n";

It produces:

Encrypted: iOKEAxaE4vIeWXBem01gHr2wdof7ZO2dld3BuR9l3Nw=

JavaScript

var mcrypt = require('mcrypt');
var MCrypt = mcrypt.MCrypt;

// Set algorithm and mode
var rijndaelEcb = new MCrypt('rijndael-128', 'ecb');

// Set up salt and IV
var salt = 'sodiumChloride12';
var iv = rijndaelEcb.generateIv();
rijndaelEcb.open(salt, iv);

/** ENCRYPTION **/
var cipher = rijndaelEcb.encrypt('This is my password');
var cipherConcat = Buffer.concat([iv, cipher]).toString('base64');
console.log('Encrypted: ' + cipherConcat);

/** DECRYPTION **/
// Convert back from base64
var ivAndCipherText = new Buffer(cipherConcat, 'base64');

// Undo concat of IV
var ivSize = rijndaelEcb.getIvSize();
iv = new Buffer(ivSize);
var cipherText = new Buffer(ivAndCipherText.length - ivSize);
ivAndCipherText.copy(iv, 0, 0, ivSize);
ivAndCipherText.copy(cipherText, 0, ivSize);

var plaintext = rijndaelEcb.decrypt(cipherText).toString();
console.log('Decrypted: ' + plaintext);

The Node version produces:

Encrypted: 834aJoVRxla/fGNACUAVFYjihAMWhOLyHllwXptNYB69sHaH+2TtnZXdwbkfZdzc
Decrypted: This is my password

Based on the fact that it decrypted the original phrase, I know the calls are working as expected, but the encrypted output is not the same as in the PHP script. Decryption logic was from this answer, but I'm more concerned with making the encryption work the same way.

Am I doing something different with the IV in Node than in PHP?

I looked at this question, but it uses the crypto module instead of the mcrypt module I am using.


Solution

  • Am I doing something different with the IV in Node than in PHP?

    Hmm. What does the code say?

    MCRYPT_MODE_ECB,

    var rijndaelEcb = new MCrypt('rijndael-128', 'ecb');

    You're using ECB mode, which doesn't use an IV. You're actually wasting CPU cycles generating one. Even if you were using CBC mode, MCRYPT_RAND is a bad constant to use here.

    var cipherConcat = Buffer.concat([iv, cipher]).toString('base64');

    You're concatenating an unused IV to your ciphertext, and complaining about an invalid result? The PHP code just returns cipher (in Node.js equivalent terms), not a concatenation of iv and cipher.


    More importantly, there are some serious cryptography flaws here that need to be addressed, starting with ECB mode as mentioned above:

    1. Do not encrypt passwords, use a password hashing algorithm. There's a huge difference.
    2. If you're going to encrypt, use authenticated encryption.
    3. Don't use mcrypt.
    4. Don't deploy home-grown crypto protocols into production or encourage other developers to do the same.

    Recommended steps:

    1. Decrypt your data with PHP, then store passwords properly.
    2. If you need encryption for any other purpose, use a high-level cryptography library, like libsodium.