Search code examples
javascriptcnode.jsaescryptojs

C - tiny-aes-c and Javascript CryptoJS interoperability


Using tiny-aes-c. Consider the following C code:

int main(int argc, char const *argv[])
{
    uint8_t key[6] = { 's','e','c','r','e','t' };
    uint8_t iv[16]  = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };

    uint8_t in[6]  = { 'm','e','s','a','g','e'};

    uint8_t out[6] = {0x17, 0x8d, 0xc3, 0xa1, 0x56, 0x34};
    struct AES_ctx ctx;

    AES_init_ctx_iv(&ctx, key, iv);
    AES_CTR_xcrypt_buffer(&ctx, in, 6);    

    printf("idx\t encrypted\t expected");
    for(int i=0 ; i<6 ; i++){
        printf("\n[%i]\t %.2x\t\t %.2x" , i , in[i], out[i]);
    }

    return 0;
}

The code encrypts a message and compares the results with the expected output. The code works fine and the output is as follows:

idx      encrypted       expected
[0]      17              17
[1]      8d              8d
[2]      c3              c3
[3]      a1              a1
[4]      56              56
[5]      34              34

I have another service, a NodeJS server which uses CryptoJS.
My question is: How can I transform the C results ({0x17, 0x8d, 0xc3, 0xa1, 0x56, 0x34}) so it will match something CryptoJS could handle?


Edit: Elaborating a bit. for the purpose of this discussion, the C result is transmitted over the network, so it should be transformed to a String. As far as I know, CryptoJS uses base64 as an input for it's AES method, decrypts to bytes that later can be converted to plain text:

var bytes  = CryptoJS.AES.decrypt(BASE_64_STRING, SECRET);
var plaintext = bytes.toString(CryptoJS.enc.Utf8);

The encrypted result for the same message + secret with CryptoJS is: U2FsdGVkX1/TAYUIFnXzC76zb+sd8ol+2DfKCkwITfY= (JS Fiddle) and changes on each run.

Update 2:
Thanks to @MDTech.us_MAN answer I've made some changes to both the JS and C code, but I'm still missing a puzzle pice.

C:

int main(int argc, char const *argv[])
{
    uint8_t key[16] = { 's','e','c','r','e','t','s','e','c','r','e','t','1','2','3','4' };
    uint8_t iv[16]  = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
    uint8_t in[7]  = { 'm','e','s','s','a','g','e'};

    struct AES_ctx ctx;

    AES_init_ctx_iv(&ctx, key, iv);
    AES_CTR_xcrypt_buffer(&ctx, in, 7);

    printf("Encrypted: ");
    for(int i=0 ; i<7 ; i++){
        printf("%.2x" , in[i]);
    }

    return 0;
}

The encrypted HEX string C output: cba9d5bc84113c, when converted to Base64 result is :y6nVvIQRPA==

On the JS side, I'm explicitly using CTR mode with no padding, and initiating (hopefully) same iv like so:

const CryptoJS = require("crypto-js");
let iv = CryptoJS.enc.Hex.parse('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); // 16 Bytes (same as the C code)
let message = CryptoJS.AES.decrypt("y6nVvIQRPA==", "secretsecret1234", { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding });
console.log(message.toString());

The decrypted result: a47172dfe151c7 and not the expected result "message".

What am I missing?


Solution

  • Thanks to @MDTech.us_MAN and this stack overflow question, I've found a solution, after fixing the mode and the padding, the difference was the way I was parsing the secret on the JS side. In the following example, the secret is parsed as a HEX string:

    const CryptoJS = require("crypto-js");
    let iv = CryptoJS.enc.Hex.parse('f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'); // 16 Bytes
    let secret = CryptoJS.enc.Hex.parse('73656372657473656372657431323334'); // 16 Bytes == "secretsecret1234"
    let message = CryptoJS.AES.decrypt("y6nVvIQRPA==", secret, { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding });
    console.log(message.toString(CryptoJS.enc.Utf8)); // -> message