Search code examples
node.jscryptography3descbc-mode

TripleDes CBC Nodejs implementation throuble


i need to replicate in Nodejs the results of the 3DS CBC encrypt in http://tripledes.online-domain-tools.com/.

This is my code:

const crypto = require('crypto');
const cipher = crypto.createCipher('des-ede3-cbc', key);
password = Buffer.from('MYPASS', 'utf8');

let encrypted = [cipher.update(password)];
encrypted.push(cipher.final());
encrypted = Buffer.concat(encryptedArr);
console.log(encrypted.toString('hex'));

The result of tripledes.online-domain-tools.com is:

Note that the result should be 59 30 20 02 a5 8c dd 5e, but my code gives me 33 97 d8 b0 e3 00 d1 53

Note that the result should be 59 30 20 02 a5 8c dd 5e, but my code gives me 33 97 d8 b0 e3 00 d1 53.

What am i missing?

Edit2: Following your suggestions, I changed my code (Also added some Tests made with the guide of the NIST Publication):

const crypto = require('crypto');
function encrypt (inputkey, keyformat, password, passwordformat) {
    let shortkey = Buffer.from(inputkey, keyformat);
    let key = Buffer.alloc(24);
    key.fill('\0');
    for (i = 0; i < shortkey.length; i++) {
        key[i] = shortkey[i];
    }
    let IV = Buffer.alloc(8);
    const cipher = crypto.createCipheriv('des-ede3-cbc', key, IV);
    password = Buffer.from(password, passwordformat);

    let encryptedArr = [cipher.update(password)];
    encryptedArr.push(cipher.final());
    encrypted = Buffer.concat(encryptedArr);
    return encrypted;
}

console.log(encrypt('1046913489980131','hex','0000000000000000','hex')); // works 
console.log(encrypt('1007103489988020','hex','0000000000000000','hex')); // works
console.log(encrypt('10071034C8980120','hex','0000000000000000','hex')); // works
console.log(encrypt('1046103489988020','hex','0000000000000000','hex')); // works
console.log(encrypt('MYKEY','utf8','MYPASS','utf8')); // fails

Every Permutation Operation Known Answer Test of the NIST works great, but several other examples (including the one of the image) just fails

The reason i'm testing with this shady page is because my service provider is using it as reference.


Solution

  • This site made me some troubles for some time , well here is the implementation it uses internally to expand the key to be a 24 bytes ! i am going to talk about tripledes but i guess this would apply to other algorithms used by this site

    step 1

    it first checks if the key entered has the length that it expects it to be, (you can find a table at the bottom of that site telling the length of key for each Encryption algorithm) if it does not it will complete with 0x00 bytes like this:

    var key;// is a string containing the bytes wich will be used to encrypt  the msg
    var nullByte = 0x00;
    var padding_needed;
    for (var i=key.length ;i < expected_key_length ; ++) 
    {padding_needed =padding_needed + nullBute.tostring(16);  }
    key = key + padding_needed
    

    so for example the length that it expects for 3DES is 24 bytes ; if you happen to enter just 15 bytes like this (112233445566778899aabbccddeeff) it will be like if you entered (112233445566778899aabbccddeeff00)

    step2

    in the case of tripledes the algorithm to expand the 16 bytes to 24 bytes key (which is the key length required by the algorithm) this site has a simple approach to do that it copies the first 8 bytes and append it to the end of the key like this

    key =key + key.substring(0,8);
    

    and that is the key that is going to be given to the 3DES encryption function to work with

    this simple approache is not used by openssl for example , open ssl uses the first 8 bytes of the MD5 of the key ,and append them to the 16 bytes of the original key to get the 24 bytes key that is required by 3DES, like this

    key = key + (MD5(key)).substring(0,8);
    

    Summary

    in that tool if you enter the key 112233445566778899AABBCCDDEEFF is the same as if you entered 112233445566778899AABBCCDDEEFF00 and same as if you entered 112233445566778899AABBCCDDEEFF001122334455667788 so to solve your problem you should give your function the complete 24 bytes of key that you gave to that site and you will surely get the same results, beacause nodejs is probably is doing the same thing as openssl does to expand the key(uses md5)

    PS if you are using the cbc mode which is your case try to specify the IV to be 8 bytes of \x00 like this "0000000000000000" the results will be the same !!

    here is a working implementation of your code you can check it in the site

        const crypto = require('crypto');
    function encrypt (inputkey, keyformat, password, passwordformat) {
        let shortkey = Buffer.from(inputkey, keyformat);
        let key = Buffer.alloc(24);
        key.fill('\0');
        for (i = 0; i < shortkey.length; i++) {
            key[i] = shortkey[i];
        }
        let IV = Buffer.alloc(8);
    
    
        var expansionStart = shortkey.length>16?shortkey.length:16;
        for (i=expansionStart;i<24;i++){
            key[i]=key[i-expansionStart];
        } 
        console.log(key);
        const cipher = crypto.createCipheriv('des-ede3-cbc', key, IV);
        password = Buffer.from(password, passwordformat);
    
        let encryptedArr = [cipher.update(password)];
        encryptedArr.push(cipher.final());
        encrypted = Buffer.concat(encryptedArr);
        return encrypted;
        }
        var enc = encrypt("112233445566778899AABBCCDDEEFF","hex","password","utf8");
        console.log(enc);