Search code examples
node.jsencryptionaes

node.js crypto aes-128-ecb encrypting a single 128 bit block results in a 256 bit value, why?


When I use node's crpto library to encrpyt a sing 128 bit block with aes-128-ecb, the result is a 256 bit value. It is my understanding with ECB that the plain text and cipher text should be the same size. Furthermore, if I encrypt a different 128 bit block with the same key, the second half of the cipher text is the same across all plain texts. This seems like some kind of block chaining, but I specifically specified ECB. Is there a way to produce a single 128 bit block of cipher text from a single 128 bit plain text and back again?

const crypto = require('crypto');

const key = Buffer.from('0c0b29abb5bcfc44ec42fb6bf04f9dfd', 'hex');
const plainData = Buffer.from('6a4e083cd50ba28d784ffcd639334910', 'hex');

const cipher = crypto.createCipheriv('aes-128-ecb', key, null);
const decipher = crypto.createDecipheriv('aes-128-ecb', key, null);

const encryptedData = Buffer.concat([
    cipher.update(plainData),
    cipher.final(),
]);

const decryptedData = Buffer.concat([
    decipher.update(encryptedData),
    decipher.final(),
]);

console.log({
    key: key.toString('hex'),
    plainData: plainData.toString('hex'),
    encryptedData: encryptedData.toString('hex'),
    decryptedData: decryptedData.toString('hex'),
});
// {
//   key: '0c0b29abb5bcfc44ec42fb6bf04f9dfd',
//   plainData: '6a4e083cd50ba28d784ffcd639334910',
//   encryptedData: '8f5a46ed1e8b62a5a23c10629786975c71f24b2d7027786c1571415309b57629',
//   decryptedData: '6a4e083cd50ba28d784ffcd639334910'
// }

Solution

  • Thanks to @dave_thompson_085 for pointing me to the documentation on how to disable padding. I added .setAutoPadding(false) to allow direct block for block encryption/decryption. Bonus, I can re-use the same Cipher/Decipher objects for subsequent blocks.

    const crypto = require('crypto');
    
    const key = Buffer.from('0c0b29abb5bcfc44ec42fb6bf04f9dfd', 'hex');
    
    const cipher = crypto.createCipheriv('aes-128-ecb', key, null);
    cipher.setAutoPadding(false); // <-- THIS
    const decipher = crypto.createDecipheriv('aes-128-ecb', key, null);
    decipher.setAutoPadding(false); // <-- AND THIS
    
    for (plainHex of [
        '6a4e083cd50ba28d784ffcd639334910',
        'f11d8086c8949372d5dc016ab7d7b21d',
        '1687976ef9ee7e6782fe1925c8e1f337',
    ]) {
        const plainData = Buffer.from(plainHex, 'hex');
        const encryptedData = cipher.update(plainData);
        const decryptedData = decipher.update(encryptedData);
    
        console.log({
            key: key.toString('hex'),
            plainData: plainData.toString('hex'),
            encryptedData: encryptedData.toString('hex'),
            decryptedData: decryptedData.toString('hex'),
        });
    }
    
    cipher.final();
    decipher.final();
    
    // {
    //     key: '0c0b29abb5bcfc44ec42fb6bf04f9dfd',
    //     plainData: '6a4e083cd50ba28d784ffcd639334910',
    //     encryptedData: '8f5a46ed1e8b62a5a23c10629786975c',
    //     decryptedData: '6a4e083cd50ba28d784ffcd639334910'
    //   }
    //   {
    //     key: '0c0b29abb5bcfc44ec42fb6bf04f9dfd',
    //     plainData: 'f11d8086c8949372d5dc016ab7d7b21d',
    //     encryptedData: 'f66f5b9e68879a732287709bb96d85b9',
    //     decryptedData: 'f11d8086c8949372d5dc016ab7d7b21d'
    //   }
    //   {
    //     key: '0c0b29abb5bcfc44ec42fb6bf04f9dfd',
    //     plainData: '1687976ef9ee7e6782fe1925c8e1f337',
    //     encryptedData: 'db108355becf71dbfd78750820c01c15',
    //     decryptedData: '1687976ef9ee7e6782fe1925c8e1f337'
    //   }