Search code examples
node.jsesp8266nodemcunode-crypto

NodeMCU node.js crypto


I was trying to set up an encrypted communication between node.js and NodeMCU. After some struggle, I was able to encrypt using node.js and decrypt it on NodeMCU. The reverse is not working. The reply by mscdex worked. Accordingly I modified the node.js code for the benefit of others. Thanks.

NodeMCU code:

crypto = require('crypto');
cipher = crypto.encrypt("AES-CBC", "0123456789abcdef", "some clear text data",'0000000000000000')
print(crypto.toHex(cipher))
//95f27285ba29aeae1e48cfc6e821b0a15a0dd6c5a1e636e10c5497c460ed057b
print(crypto.toBase64(cipher))
//lfJyhboprq4eSM/G6CGwoVoN1sWh5jbhDFSXxGDtBXs=

Node.js working code:

var crypto=require('crypto'); //crypto module is required
algorithm = 'aes-128-cbc'; //define algorithm
password = '0123456789abcdef'; //define key
iv='0000000000000000'; // define vector for cbc.. Must be 16 char length
function encrypt(text,opts) {
  if(typeof opts === 'undefined') opts={}
  // if opts is not defined, set empty list
  var cipher = crypto.createCipheriv(algorithm,password,iv)
  // create cipher
  //if setAutoPadding is undefined or set as true set setAutoPadding as true
  if(opts['setAutoPadding'] || typeof opts['setAutoPadding'] !== 'undefined') {
    cipher.setAutoPadding(opts['setAutoPadding']);
    //if encoding is defined, then set it as encoding else set default hex
    if(opts['encoding'] && typeof opts['encoding'] !== 'undefined') {
      var crypted = cipher.update(text,'utf8',opts['encoding'])
      crypted += cipher.final(opts['encoding']);
      return crypted
    } else {
      var crypted = cipher.update(text,'utf8','hex')
      crypted += cipher.final('hex');
      return crypted;
    }
  }

  function decrypt(text,opts) {
    if(typeof opts === 'undefined') opts={}
    // if opts is not defined, set empty list
    var decipher = crypto.createDecipheriv(algorithm,password,iv)
    // create cipher
    //if setAutoPadding is undefined or set as true set setAutoPadding as true
    if(opts['setAutoPadding'] || typeof opts['setAutoPadding'] !== 'undefined') {
      decipher.setAutoPadding(opts['setAutoPadding']);
    }
    var dec; // define a local variable
    //if encoding is defined, then set it as encoding else set default hex
    if(opts['encoding'] && typeof opts['encoding'] !== 'undefined') {
      dec = decipher.update(text,opts['encoding'],'utf8')
    } else {
      dec = decipher.update(text,'hex','utf8')
    }
    dec += decipher.final('utf8');
    return dec;
  }
  var hw = encrypt("some clear text data",{'encoding':'base64'})
  //encrypt with base64 encoding, padding true
  console.log(hw) // prints base64 encoded encrypted string
  console.log('Node.js-base64: ',decrypt(hw,{'encoding':'base64'}))
  // outputs some clear text data
  hw = encrypt("some clear text data")
  // encrypt default encoding hex, defaule padding true
  console.log(hw) // prints hex encoded encrypted string
  console.log('Node.js-hex: ',decrypt(hw)) // outputs some clear text data
  jw='lfJyhboprq4eSM/G6CGwoVoN1sWh5jbhDFSXxGDtBXs='
  // NodeMCU base64 encoded, padding false encrypted string
  console.log('NodeMCU-base64: ',decrypt(jw, { 'setAutoPadding':false,'encoding':'base64'}));
  // outputs some clear text data
  jw='95f27285ba29aeae1e48cfc6e821b0a15a0dd6c5a1e636e10c5497c460ed057b'
  // nodeMCU, hex encoded, padding false encrypted string
  console.log('NodeMCU-hex: ',decrypt(jw,{'setAutoPadding':false}));
  // outputs some clear text data
  console.log("over")

Now again NodeMCU side testing:

  cipher=encoder.fromBase64("lfJyhboprq4eSM/G6CGwoYmVZaNMY5xL2kR7V2E1Aho=")
  key="0123456789abcdef"
  print(crypto.decrypt("AES-CBC", key, cipher,'0000000000000000'))
  some clear text data
  cipher=encoder.fromBase64("lfJyhboprq4eSM/G6CGwoVoN1sWh5jbhDFSXxGDtBXs=")
  key="0123456789abcdef"
  print(crypto.decrypt("AES-CBC", key, cipher,'0000000000000000'))
  some clear text data

What was working?

Node.js encryption is getting decrypted on NodeMCU, even though the encrypted string is a bit different.

What was not working?

The encrypted string of the NodeMCU is not getting decrypted by node.js. I am getting the following error:

crypto.js:153
var ret = this._handle.final();

Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length at Error (native) at Decipheriv.Cipher.final (crypto.js:153:26) at decrypt (/home/pi/rampion/nodejs/test2.js:22:19) at Object. (/home/pi/rampion/nodejs/test2.js:43:13) at Module._compile (module.js:413:34) at Object.Module._extensions..js (module.js:422:10) at Module.load (module.js:357:32) at Function.Module._load (module.js:314:12) at Function.Module.runMain (module.js:447:10) at startup (node.js:146:18)

The error was due to the reason highlighted by mscdex in his reply.

NodeMCU does not utilize PKCS padding but node's crypto module does use/expect it by default, so you need to disable it before calling .update() when decrypting by decipher.setAutoPadding(false); calling


Solution

  • NodeMCU does not utilize PKCS padding but node's crypto module does use/expect it by default, so you need to disable it before calling .update() when decrypting:

    function decrypt(text){
      var decipher = crypto.createDecipheriv(algorithm,password,iv)
      decipher.setAutoPadding(false);
      var dec = decipher.update(text,'hex','utf8')
      dec += decipher.final('utf8');
      return dec;
    }
    
    function decryptB(text){
      var decipher = crypto.createDecipheriv(algorithm,password,iv)
      decipher.setAutoPadding(false);
      var dec = decipher.update(text,'base64','utf8')
      dec += decipher.final('utf8');
      return dec;
    }
    

    After that change, you will be able to decrypt the commented hex and base64 values in your NodeMCU code.