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 pares 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?
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 pares 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?
Share Improve this question edited May 13, 2018 at 9:23 Shlomi Schwartz asked May 10, 2018 at 14:09 Shlomi SchwartzShlomi Schwartz 8,91330 gold badges119 silver badges198 bronze badges 4-
what type can
CryptoJS
handle? how you get the C result? in what format? your question is unclear to me. – apple apple Commented May 10, 2018 at 14:14 - Please see my edits :) – Shlomi Schwartz Commented May 10, 2018 at 14:23
- the most simple solution I think up now is transmit the C result in base64. or in hex then convert to base64 if the C part cannot change. – apple apple Commented May 10, 2018 at 14:29
- I tried it, however the output of cryptoJS is longer than what I get. – Shlomi Schwartz Commented May 10, 2018 at 14:30
3 Answers
Reset to default 7 +500You should read the CryptoJS documentation more carefully. By default it uses CBC mode for the encryption so you should change your tiny-AES implementation to use that.
CryptoJS supports the following modes:
- CBC (the default)
Also note that CryptoJS has padding enabled by default, while tiny-AES doesn't have it at all. Therefore, messages must be multiples of 16. (Or you can manually use your own padding implementation)
No padding is provided so for CBC and ECB all buffers should be mutiples of 16 bytes. For padding PKCS7 is remendable.
Then, notice that CryptoJS autoselects the AES variant by the key size:
CryptoJS supports AES-128, AES-192, and AES-256. It will pick the variant by the size of the key you pass in. If you use a passphrase, then it will generate a 256-bit key.
So, you must consider all of these factors in your tiny-AES code.
You are doing two things
- Encrypt
- Convert to base64
Upon receiving, you must do the reverse operations of both of these, in the reverse order they were applied before transmitting
- Convert from base64
- Decrypt
You can also swap the order of the steps, but then the order must be swapped on both the transmitting and receiving sides.
Also make sure the secret is on the same format on both sides.
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