2

I am getting this error:

Warning: mcrypt_decrypt(): The IV parameter must be as long as the blocksize

It's from encrypting a variable with code that is mostly the same as the code on the php manual site (http://php.net/manual/en/function.mcrypt-encrypt.php). It was working when it was all together (I split it into two to use as include() files and added convenient input and output variables). It encrypts a value, posts it on a $_GET variable, and then on the next page load it decrypts it. However, upon decryption, I get the error. I'm guessing it might have something to do with saving the encrypted information on a $_GET variable, then reading it. The encrypted text and $_GET identifier in the URL, in one instance, looks like this: Last_Song_ID=mIyFkMdMgVgSZU18wD/vJ3bI8qf++ea1/NtGrsajKd4=

In this file (the error occurs at $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec); near the bottom:

include('key.php');

$ciphertext_base64 = $de_in;

$ciphertext_dec = base64_decode($ciphertext_base64);

$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
echo $iv_size . "<br>";

# retrieves the IV, iv_size should be created using mcrypt_get_iv_size()
$iv_dec = substr($ciphertext_dec, 0, $iv_size);

# retrieves the cipher text (everything except the $iv_size in the front)
$ciphertext_dec = substr($ciphertext_dec, $iv_size);

# may remove 00h valued characters from end of plain text
$plaintext_utf8_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,
                                     $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

$de_out = $plaintext_utf8_dec;

The encrypted text comes from a $_GET variable in the primary file that comes from the following:

<?php
    # --- ENCRYPTION ---

    # the key should be random binary, use scrypt, bcrypt or PBKDF2 to
    # convert a string into a key
    # key is specified using hexadecimal
    include('key.php');

    # show key size use either 16, 24 or 32 byte keys for AES-128, 192
    # and 256 respectively
    $key_size =  strlen($key);
    //echo "Key size: " . $key_size . "\n";

    if(!isset($en_in))
    $en_in = "No Input";

    # create a random IV to use with CBC encoding
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    echo $iv_size . "<br>";
    $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);

    # use an explicit encoding for the plain text
    $plaintext_utf8 = utf8_encode($en_in);

    # creates a cipher text compatible with AES (Rijndael block size = 128)
    # to keep the text confidential 
    # only suitable for encoded input that never ends with value 00h
    # (because of default zero padding)
    $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key,
                                 $plaintext_utf8, MCRYPT_MODE_CBC, $iv);

    # prepend the IV for it to be available for decryption
    $ciphertext = $iv . $ciphertext;

    # encode the resulting cipher text so it can be represented by a string
    $ciphertext_base64 = base64_encode($ciphertext);

    $en_out = $ciphertext_base64;


?>
Mark Fox
  • 8,694
  • 9
  • 53
  • 75
JVE999
  • 3,327
  • 10
  • 54
  • 89
  • Just a thought, what happens if `$ciphertext_dec` (for whatever reason) is smaller than `$iv_size`? You don't check that at any point. You should be doing that around here: `$iv_dec = substr($ciphertext_dec, 0, $iv_size);` – Francisco Presencia Jul 14 '13 at 00:47
  • Another note, you have `$en_in` and `$en_out` but then `$de_in` and `$de_out`, assuming `$en` and `$de` are language names, is this to be expected or is it a typo? – Francisco Presencia Jul 14 '13 at 00:50
  • 1
    I'm assuming the encryption always results in a variable that's longer than $iv_size (which in this case is '16') – JVE999 Jul 14 '13 at 00:50
  • 1
    @FranciscoPresencia it just means encryption in and decryption in – JVE999 Jul 14 '13 at 00:51

1 Answers1

4

You cannot pass a base 64 via GET parameter, since these are the valid base64 characters, which can clash with the GET parameters:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

So you might not be retrieving the whole GET as expected, but only a part. You might want to do this to check that:

Last line of encoding:

echo strlen($en_out);

At the beginning of the encoding:

echo strlen($de_in);

If the sizes don't match each other, there's your problem, the whole thing is not being passed properly for the reasons mentioned and linked above.

EDIT: About the solution, both the first and second answer in the question linked are pretty good.

Community
  • 1
  • 1
Francisco Presencia
  • 8,732
  • 6
  • 46
  • 90
  • Absolutely perfect! Just as I suspected, and the answer I did not know, either. Thanks! – JVE999 Jul 14 '13 at 01:26
  • 1
    I've been doing an encryption system myself for fun using a very similar script to yours ( [secretdiary.org](http://secretdiary.org) disclaimer: my page). It's a bit sad that most problems with encryption come from small things like this, so difficult to track down and in which you learn so little. Glad to help (; – Francisco Presencia Jul 14 '13 at 01:32