Matching PHP's mcrypt_encrypt() with node.js crypto.createCipher()

Problem

I have a receiving application that expects a string from a PHP producer that looks like this:

<?php
        $shared_secret = 'secret';
        $data = 'whatever';

        # Newline added for viewing convenience only
        echo bin2hex(mcrypt_encrypt(MCRYPT_BLOWFISH, $shared_secret, $data,
                     MCRYPT_MODE_ECB)) . "\n";

        # -> 05c3febb9970204a
?>

The receiver is expensive to change.

I am building another producer using node.js and I can't get my JavaScript code to produce the same output:

  var data, encrypt, sharedSecret;

  sharedSecret = 'secret';
  data = 'whatever';

  encrypt = function(d) {
    var cipher, crypto;
    crypto = require('crypto');
    cipher = crypto.createCipher('bf-ecb', sharedSecret, '\0\0\0\0\0\0\0\0');
    cipher.update(d);
    return cipher.final('hex');
  };

  console.log(encrypt(data));

  // -> 35c9801f2afca332

I chose the 'bf-ecb' cipher, because I think that's blowfish in ECB mode. I provided 8 null bytes as the IV, because the PHP documentation for mcrypt_encrypt says that if you omit the IV, it uses all null bytes, and mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB) answers 8. I chose 'hex' because I think it provides the same representation as PHP'2 bin2hex().

What should I do to my JavaScript code to match the output of the PHP code?

Problem courtesy of: sheldonh

Solution

There were only four problems with my code:

  • The output doesn't need to be identical. It just needs to decipher to the same plaintext. Given that node and PHP pad differently, chasing identical ciphers was silly of me.
  • ECB mode doesn't use an IV. I was mislead my PHP's mcrypt_get_iv_size(MCRYPT_BLOWFISH, MCRYPT_MODE_ECB) which reports 8 instead of saying "you are a fool".
  • I mistyped crypto.createCipheriv(), which uses the key as given. crypto.createCipher() uses a derivation of the given key (md5, I think).
  • The return value of update() must not be thrown away.

So the working solution is:

  var data, encrypt, sharedSecret;

  sharedSecret = 'secret';
  data = 'whatever';

  encrypt = function(d) {
    var cipher, crypto;
    crypto = require('crypto');
    cipher = crypto.createCipheriv('bf-ecb', sharedSecret, '');
    return cipher.update(d, 'utf8', 'hex') + cipher.final('hex');
  };

  console.log(encrypt(data));
Solution courtesy of: sheldonh

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.