16

I have question about AES key and IV length.

First of all, if, for example, I'm using drugs OpenSSL extension and openssl_encrypt() method, I can clearly see that key for 256-bit AES should be 32 bytes, and IV throws warning if it's different than 16 bytes. I can understand that, and everything is fine.

However, in CryptoJS library the key and IV length is frustrating. This is some example:

var text = "test",
    key  = "us5N0PxHAWuIgb0/Qc2sh5OdWBbXGady",
    iv   = "zAvR2NI87bBx746n";

key = CryptoJS.enc.Base64.parse(key);
iv  = CryptoJS.enc.Base64.parse(iv);

crypted = CryptoJS.AES.encrypt(text, key, { iv: iv });

where key is 32 bytes, IV is 16. CryptoJS requires to parse it, and after CryptoJS.enc.Base64.parse() I get 48 and 24 bytes accordingly. I expect that those values will get truncated to required 256-bit AES length, and further expansion to n bytes will be irrelevant, and so resulting ciphertext will be the same.

But that's not actually happening. When I pass to CryptoJS.AES.encrypt() larger size key and even IV, it's producing different output. So my question is, why? What is the difference between CryptoJS library and OpenSSL in this case?

Damaged Organic
  • 8,175
  • 6
  • 58
  • 84

1 Answers1

27

Looks like I've got it.

If you tend to pass custom key and IV in using CryptoJS, make sure that (assuming that CryptoJS.enc.Base64.parse() gives HEX string, which is used in CryptoJS.AES.encrypt()) keys length are matching the block size.

Taking this example, with Base64 key and iv (length=22), which CryptoJS encrypts as AES-256:

var message = "some_secret_message";

var key = "6Le0DgMTAAAAANokdEEial"; //length=22
var iv  = "mHGFxENnZLbienLyANoi.e"; //length=22

key = CryptoJS.enc.Base64.parse(key);
//key is now e8b7b40e031300000000da247441226a, length=32
iv = CryptoJS.enc.Base64.parse(iv);
//iv is now 987185c4436764b6e27a72f2fffffffd, length=32

var cipherData = CryptoJS.AES.encrypt(message, key, { iv: iv });

var data = CryptoJS.AES.decrypt(cipherData, key, { iv: iv });
//data contains "some_secret_message"

Length of the key is 32 bytes for AES-256. (16 bytes if you want to get AES-128. If more, CryptoJS will switch to higher key length). In other case on decrypt you will get an empty message. Example:

var message = "some_secret_message";
        
var key = "6Le0DgMTAAAAANokdEEial1"; //length=23
var iv  = "mHGFxENnZLbienLyANoi.e"; //length=22
        
key = CryptoJS.enc.Base64.parse(key); // length = 17 bytes
//key is now e8b7b40e031300000000da247441226a5d, length=34 (hex encoded)
iv = CryptoJS.enc.Base64.parse(iv); // length = 16 bytes
//iv is now 987185c4436764b6e27a72f2fffffffd, length=32 (hex encoded)
        
var cipherData = CryptoJS.AES.encrypt(message, key, { iv: iv });
        
var data = CryptoJS.AES.decrypt(cipherData, key, { iv: iv });
//data contains "" - an empty string

Also, from what I can see, only x % 8 == 0 bytes of such use case gives valid result.

Length of IV should be 22 bytes (when Base64 encoded), and while transforming with CryptoJS.enc.Base64.parse() you will get 16 bytes (32 hex encoded), which is max for AES-256 block size. Everything more than that will get truncated.

var message = "some_secret_message";

var key = "6Le0DgMTAAAAANokdEEial"; //length=22
var iv  = "mHGFxENnZLbienLyANoi.e"; //length=22

key = CryptoJS.enc.Base64.parse(key); // length=16 bytes
//key is now e8b7b40e031300000000da247441226a5d, length=32 (hex encoded)
iv = CryptoJS.enc.Base64.parse(iv); // length=16 bytes
//iv is now 987185c4436764b6e27a72f2fffffffd, length=32 (hex encoded)

var cipherData = CryptoJS.AES.encrypt(message, key, { iv: iv });

var key = "6Le0DgMTAAAAANokdEEial"; //length=22
var iv  = "mHGFxENnZLbienLyANoi.e123"; //length=25

key = CryptoJS.enc.Base64.parse(key); // length = 16 bytes
//key is now e8b7b40e031300000000da247441226a5d, length=32 (hex encoded)
iv = CryptoJS.enc.Base64.parse(iv); // length = 18 bytes
//iv is now 987185c4436764b6e27a72f2fffffffded76, length=36 (hex encoded)

var data = CryptoJS.AES.decrypt(cipherData, key, { iv: iv }); //data contains "some_secret_message", so additional "123" in IV is irrelevant.
Damaged Organic
  • 8,175
  • 6
  • 58
  • 84
  • 2
    1. There is something wrong with your numbers. AES has a block size of 128-bit which is also the expected size of the IV. AES is not specified for IVs larger or smaller than 16 bytes. IV size is not the same as key size. 2. 22 bytes is not a valid base64 encoded string. It must be divisible by 4 (with optional pad chars). – Artjom B. Apr 10 '15 at 13:00
  • I object. AES has block size 128 if it's AES-128. In case of AES-256 - block size is, obviously, 256 bits, which is 32 bytes, which is exactly what you get by `CryptoJS.enc.Base64.parse()` of 22 byte Base64 string. According to specification and algorithm, IV is exactly block size length, which is 32 bytes with AES-256. – Damaged Organic Apr 10 '15 at 14:05
  • For deeper explanation, please, refer to http://security.stackexchange.com/questions/15740/what-are-the-variables-of-aes – Damaged Organic Apr 10 '15 at 14:26
  • If you refer to something, make sure that it supports your claim: sqrt(23) writes: *"AES happens to be one amongst many [...], with a constant block size of 16 bytes"* and *"Initialization Vector, which very often matches the block size of the cipher"*. It doesn't support your claim. Key size and block size are different things. The 256 in AES-256 refers to the key size. – Artjom B. Apr 10 '15 at 14:33
  • It's for AES-128. And it says,"Initialization Vector, which very often matches the block size of the cipher". For block cipher modes of operation, the IV is usually as large as the block size of the cipher. So, looks like 32 bytes to me for 256 bits AES. – Damaged Organic Apr 10 '15 at 14:49
  • There really is no AES with a block size of 32 bytes. Only Rijndael which AES is based on additionally defines 32 byte block sizes, but AES does not and the CryptoJS implementation thereof also does not. If you don't believe me, look it up on wikipedia ([AES](http://en.wikipedia.org/wiki/Advanced_Encryption_Standard) and [CBC-mode](http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC)). IV only makes sense on blocks and has nothing to do with the key size. – Artjom B. Apr 10 '15 at 14:58
  • By the way, if you convert 22 bytes that are base64 encoded into binary, you get around 16 bytes not 32 bytes. – Artjom B. Apr 10 '15 at 15:01
  • Allright, I hear you. But in CryptoJS docs they say, "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.". And it's not gets incoded into binary, it's HEX, and I get exactly 32 bytes... – Damaged Organic Apr 10 '15 at 15:04
  • I see. In cryptography it is always assumed that you talk about binary encoding if you don't qualify how the bytes are represented. Please make that clear in your answer. – Artjom B. Apr 10 '15 at 15:07
  • @Artjom B: I've updated my answer with encoding info and examples. Is it accurate now? – Damaged Organic Apr 13 '15 at 10:13