0

I'm trying to use node.js' crypt module to decrypt some files that were encrypted by another program that used the openssl library in a rather non-standard library. By non-standard I mean that the number of rounds and location of the salt differs from the defaults used by openssl. So as a result I am extracting the salt first and then creating a ReadStream on the resulting description before trying to do that actual decryption.

I have two routines. The first one uses decrypt.update and decrypt.final to perform the decryption. I am able to decrypt files this way. The second uses pipe to perform the decryption. When I try to use it I get this error:

The error I get when I try to run my code is:

digital envelope routines:EVP_DecryptFinal_ex:wrong final block length

The working and failing function are below. The "binary_concat" function referenced does the equivalent of a+b for binary strings - it took me a couple hours of debugging before I discovered that a+b doesn't work properly!

function do_decrypt_works(infile,password,salt) {
    var outfile = fs.createWriteStream("/tmp/test.out")
    var text = fs.readFileSync(infile_filename).slice(8) // hack since we aren't using infile in this case
    var rounds = 28
    data00 = binary_concat(password,salt,"")

    var hash1 = do_rounds(data00)
    var hash1a = binary_concat(hash1,password,salt)
    var hash2 = do_rounds(hash1a,password,salt)
    var hash2a = binary_concat(hash2,password,salt)
    var hash3 = do_rounds(hash2a,password,salt)

    var key = binary_concat(hash1,hash2,"")
    var iv = hash3

    var decrypt = crypto.createDecipheriv('aes-256-cbc', key, iv)

    var content = decrypt.update(text, "binary", "binary");
    content += decrypt.final("binary");
}

function do_decrypt_fails(infile,password,salt) {
    var outfile = fs.createWriteStream("/tmp/test.out")
    var rounds = 28
    data00 = binary_concat(password,salt,"")

    var hash1 = do_rounds(data00)
    var hash1a = binary_concat(hash1,password,salt)
    var hash2 = do_rounds(hash1a,password,salt)
    var hash2a = binary_concat(hash2,password,salt)
    var hash3 = do_rounds(hash2a,password,salt)

    var key = binary_concat(hash1,hash2,"")
    var iv = hash3

    var decrypt = crypto.createDecipheriv('aes-256-cbc', key, iv)
    infile.pipe(decrypt).pipe(outfile)
}

According to the documentation, both createDecipher and createDecipheriv return an instance of class Decipher which can be used with either of the above techniques.

Sources:

First

Second

Third

Community
  • 1
  • 1
Michael
  • 9,060
  • 14
  • 61
  • 123

1 Answers1

0

Yes, you are looping (i = 1; i < rounds; i++) but then just taking the first three digests anyway (the first 48 bytes of result, which ends up much longer than 48 bytes with 18 passes through the loop concatenating over and over again to result).

Anyway, by my reading of the algorithm you are looping in the wrong place. Its looks like the first function you show would work fine, only, you need to add the iteration count into the hashing with something like this:

function md5(data, count) {
  var hash = crypto.createHash('md5');
  hash.update(data);
  for i = 0; i < count; i++ // PSEUDO-CODE !!!
      hash.update(hash)     // PSEUDO-CODE re-hash the hash
  return new Buffer(hash.digest('hex'), 'hex');
}
Jim Flood
  • 8,144
  • 3
  • 36
  • 48
  • I'm a little confused by the whole rounds thing. Why does it say more rounds makes it more secure if the extra rounds get concatenated and not used? Anyway, also found a couple of other issues... latest is it appears hash.digest is only create a 16 byte value but it should be 32 bytes long. The first 16 match exactly what i expect, so I don't think it's an encoding issue. (Comparing against the output of EVP_BytesToKey, where is it getting the extra 16 bytes?) – Michael Jul 01 '16 at 00:08
  • There's definitely something wrong with the KEY/IV calculation: comparing against the output of EVP_BytesToKey only the first round matches! – Michael Jul 01 '16 at 00:27
  • Oh wait, I think I see... you are doing "hash *rounds* number of times" three times, not one? – Michael Jul 01 '16 at 00:47
  • Ok, so the hashing is now being done correctly. And it appears I can decrypt the file if I first load it into memory. But I still have my original problem - if I try to run it through the pipe, I still get the "wrong final block length" error. – Michael Jul 01 '16 at 00:55
  • 18 rounds means you take the value, hash it, then hash that hash, then hash the hash, so it's a total of 18 hashes. The reason to do multiple rounds is just to take time. It doesn't make the bits any more secure. More time makes brute force (i.e. trying lots of passwords) take a *lot* more time. – Jim Flood Jul 02 '16 at 00:25
  • @Michael so the rounds of hashing just slow everything down. You only concatenate to make the bit string longer, if you need more bits that the size of the hash. If the hash is 128 bits, and you need 256 + 128, then you have to concatenate. But each of those segments is slowed down one at a time by the rounds count. – Jim Flood Jul 02 '16 at 00:27
  • @Michael when you write/read the pipe make sure it is binary, and not some character encoding, e.g. UTF-8. Sending binary ciphertext as "character data" can definitely modify certain byte sequences, corrupting the ciphertext en route. – Jim Flood Jul 02 '16 at 00:28