9

When using CryptoJS.AES.encrypt how does it come up with an Initialization Vector if the third argument is not passed to the function? Is there a way to get it out of the encrypted string?

The reason I need this is I need to decrypt something CryptoJS.AES.encrypt returned using Lua, but I only have the key that was provided.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
warspyking
  • 3,045
  • 4
  • 20
  • 37
  • It is still unclear to me and I am not sure how to find an answer. Still looking for help :( – warspyking Feb 19 '16 at 20:21
  • Well, Crypto-js is open source, so you could easily look into the source code. I've also answered related things ([1](http://stackoverflow.com/a/27971983/1816580) and [2](http://stackoverflow.com/a/33942660/1816580) and [3](http://stackoverflow.com/a/28361216/1816580)). It's basically OpenSSL's EVP_BytesToKey function. – Artjom B. Feb 19 '16 at 21:58
  • @Artjom B. So you're saying if I remade EVP_BytesToKey in say, Lua for example, I could provide the password given to CryptoJS.AES.Encrypt and get both the key and iv from it? – warspyking Feb 19 '16 at 22:05
  • Yes, and it's rather easy since it's only a couple of invocations of MD5. – Artjom B. Feb 19 '16 at 22:07
  • @Artjom B. What about the random salt you mentioned in one of your answers? I do not know the salt that was generated. – warspyking Feb 19 '16 at 22:07
  • It can be retrieved from the ciphertext object that is created. I'll write an answer tomorrow. – Artjom B. Feb 19 '16 at 22:08
  • @Artjom B. That would be great, thanks! – warspyking Feb 19 '16 at 22:09
  • What do you mean by *dehash*? Remember not to confuse keys and passwords. What exactly do you have? – Artjom B. Feb 19 '16 at 22:11
  • @Artjom B. Well I was looking at the source code of https://www.protectedtext.com/ and it appears they hash it using AES.Encrypt (as far as I can tell). I'm using a get request to get the html of a page, and need to dehash the text that is returned (which is in the html) using just the password given. I was under the impression that the second argument given to AES.Encrypt was a key though? Was I wrong? – warspyking Feb 19 '16 at 22:14
  • Perhaps a better term was decrypt, I edited the question. – warspyking Feb 19 '16 at 22:15
  • @Artjom B. Not to be pushy but I fear you forgot about me, I have no problem if you aren't ready to give an answer yet but I'm just notifying in case you have forgotten. – warspyking Feb 21 '16 at 13:19
  • 1
    Yes, I've forgotten. – Artjom B. Feb 21 '16 at 13:24
  • @Artjom B. I guess it's a good thing I notified you then :D – warspyking Feb 21 '16 at 13:25
  • [OpenSSL 1.1.0c changed the digest algorithm](http://stackoverflow.com/q/39637388/608639) used in some internal components. Formerly, MD5 was used, and 1.1.0 switched to SHA256. Be careful the change is not affecting you in both `EVP_BytesToKey` and commands like `openssl enc`. – jww Jan 26 '17 at 16:21

1 Answers1

17

CryptoJS' CryptoJS.<BlockCipher>.encrypt has two modes of encryption.

  1. If you pass in a key that is not a string, but rather a WordArray (CryptoJS's internal representation format for binary data), the key is taken as-is. This mode expects an IV for all modes of operation except ECB, which doesn't use an IV, so you don't have to specify one. If no IV is passed, it will default (through some JavaScript magic) to a zero filled IV (consisting of a full block of 0x00 bytes).

  2. If you pass in a "key" that is a string, it will assume the "key" is a password. In order to derive a key from the password, it uses the OpenSSL-compatible derivation function EVP_BytesToKey. This mode generates a new 8 byte random salt and uses it along with the password to generate a key and IV. Even if you explicitly pass in an IV, it won't be used.

     CryptoJS.AES.encrypt(msg, password).toString()
    

    results in a Base64-encoded ciphertext that contains the string "Salted__" at the beginning followed by the 8 byte salt and the actual ciphertext. You can explicitly split this before use with:

     var ct = CryptoJS.AES.encrypt(msg, password);
     var saltHex = ct.salt.toString();     // random salt
     var ctHex = ct.ciphertext.toString(); // actual ciphertext
     var ivHex = ct.iv.toString();         // generated IV
    

    If you need to recreate the same key derivation. Have a look at the code and the specification.

    Keys should have high entropy and be indistinguishable from random noise, which makes it hard for them to be brute-forced. The above mentioned EVP_BytesToKey is not secure, because MD5 hashing is very fast, which enables an attacker to brute-force the password. You either need to use a really long password (20-30 characters) or use an appropriate key derivation function such as PBKDF2, which CryptoJS provides.

hacker1024
  • 3,186
  • 1
  • 11
  • 31
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • Okay so I am able to undo base 64, so you say the 8 byte salt is after Salted__? I have no idea how to decrypt this O_O. I'll figure something out, thanks for the info though, it was very useful. – warspyking Feb 21 '16 at 15:27
  • I'm still not entirely sure what you got. If I understand you correctly, you're trying to decrypt a ciphertext in CryptoJS that you got from a Lua application. If you have access to the Lua code, then I suggest that you look closely how the encryption was done and then you can decide how the corresponding decryption would look like in CryptoJS. Also, you don't have to replicate EVP_BytesToKey for decryption, because `CryptoJS.AES.decrypt` supports the exact same methods as `CryptoJS.AES.encrypt`. Also, AES keys have a specific length, but passwords do not. How long is your key? – Artjom B. Feb 21 '16 at 15:33
  • Here's exactly what I'm doing: 1. Setting up protected text with protectedtext.com 2. Giving it a password 3. (I believe) the site puts it through that hash with my password 4. I use a get request to get the hashed content some time later on 5. I need to use Lua code to get it from the hashed (and base 64) state back to a readable human format. – warspyking Feb 21 '16 at 15:54
  • The source code is rather confusing, I'm not the best in JavaScript, I have no idea what hasher.create is doing ,nor what cfg is. I feel like I'm getting beyond the scope of my original question however. – warspyking Feb 21 '16 at 17:17
  • Also the source code is the key generation, how do it get the iv? – warspyking Feb 21 '16 at 17:18
  • The output of `EVP_BytesToKey` is requested with the size of IV size + key size. The **last** 128 bit of the output are used for the IV and the **first** bits for the key. The default key size is 256 bit for AES. Check out [`OpenSSLKdf` in cipher-core.js](https://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/cipher-core.js) and the [`keySize` property in aes.js](https://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/aes.js) [previous version of this comment was erroneous.] – Artjom B. Feb 21 '16 at 17:55