views:

588

answers:

2

I have a code that goes something like:

$cipher_alg = MCRYPT_RIJNDAEL_128;
$decrypted_string = mcrypt_decrypt($cipher_alg, $key, 
$encrypted_string , MCRYPT_MODE_CBC, trim(hex2bin(trim($hexiv))));

I worry that in the process of decoding the mcrypt_decrypt will introduce a gratuitous whitespace or null characters at the back or front of the $decrypted_string.

So should I trim it?

Note: I could have run the code and find this out. But since I can never run enough samples to prove ( or disprove) my point, I want some concrete and theoretical answers, probably based on the inner working of the mcrypt_decrypt algorithm. Another reason I ask is that I believe this is going to help others.

Note 2: Notwithstanding with the answer below, it seems that the examples here do use trimming to get the correct decrypted string.

+2  A: 

Actually both mcrypt_encrypt() and mcrypt_decrypt() as well as the other en-/decryption functons (like mcrypt_generic() or mdecrypt_generic()) do pad the $data parameter to a length of n * <<blocksize>>. The padding character is the NUL character (\x0 or \0) whereas the <<blocksize>> depends on the cipher and the block cipher modes used. You should have a look at Block cipher modes of operation and Padding (cryptography).

The following is the output of mcrypt_get_block_size() for each of the available ciphers and modes on my machine. Obviously the function does not take into account that modes such as CFB, OFB and CTR do not require any special measures to handle messages whose lengths are not multiples of the block size, since they all work by XORing the plaintext with the output of the block cipher (quote from Wikipedia). CBC which is used in your example always requires that the final block is padded before encryption.

cast-128
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported
gost
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported
rijndael-128
    cbc: 16 bytes
    cfb: 16 bytes
    ctr: 16 bytes
    ecb: 16 bytes
    ncfb: 16 bytes
    nofb: 16 bytes
    ofb: 16 bytes
    stream: not supported
twofish
    cbc: 16 bytes
    cfb: 16 bytes
    ctr: 16 bytes
    ecb: 16 bytes
    ncfb: 16 bytes
    nofb: 16 bytes
    ofb: 16 bytes
    stream: not supported
arcfour
    cbc: not supported
    cfb: not supported
    ctr: not supported
    ecb: not supported
    ncfb: not supported
    nofb: not supported
    ofb: not supported
    stream: 1 bytes
cast-256
    cbc: 16 bytes
    cfb: 16 bytes
    ctr: 16 bytes
    ecb: 16 bytes
    ncfb: 16 bytes
    nofb: 16 bytes
    ofb: 16 bytes
    stream: not supported
loki97
    cbc: 16 bytes
    cfb: 16 bytes
    ctr: 16 bytes
    ecb: 16 bytes
    ncfb: 16 bytes
    nofb: 16 bytes
    ofb: 16 bytes
    stream: not supported
rijndael-192
    cbc: 24 bytes
    cfb: 24 bytes
    ctr: 24 bytes
    ecb: 24 bytes
    ncfb: 24 bytes
    nofb: 24 bytes
    ofb: 24 bytes
    stream: not supported
saferplus
    cbc: 16 bytes
    cfb: 16 bytes
    ctr: 16 bytes
    ecb: 16 bytes
    ncfb: 16 bytes
    nofb: 16 bytes
    ofb: 16 bytes
    stream: not supported
wake
    cbc: not supported
    cfb: not supported
    ctr: not supported
    ecb: not supported
    ncfb: not supported
    nofb: not supported
    ofb: not supported
    stream: 1 bytes
blowfish-compat
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported
des
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported
rijndael-256
    cbc: 32 bytes
    cfb: 32 bytes
    ctr: 32 bytes
    ecb: 32 bytes
    ncfb: 32 bytes
    nofb: 32 bytes
    ofb: 32 bytes
    stream: not supported
serpent
    cbc: 16 bytes
    cfb: 16 bytes
    ctr: 16 bytes
    ecb: 16 bytes
    ncfb: 16 bytes
    nofb: 16 bytes
    ofb: 16 bytes
    stream: not supported
xtea
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported
blowfish
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported
enigma
    cbc: not supported
    cfb: not supported
    ctr: not supported
    ecb: not supported
    ncfb: not supported
    nofb: not supported
    ofb: not supported
    stream: 1 bytes
rc2
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported
tripledes
    cbc: 8 bytes
    cfb: 8 bytes
    ctr: 8 bytes
    ecb: 8 bytes
    ncfb: 8 bytes
    nofb: 8 bytes
    ofb: 8 bytes
    stream: not supported

Therefore you have to rtrim() the output of the decryption functions to get the original string if your cipher operates on fixed length blocks:

$output = rtrim($decrypted, "\0");
Stefan Gehrig
This is the correct solution. It is very important to use double quotes and not single quotes around `\0`!
shadowhand
Btw, it is very unlikely that using `trim(..., "\0")` would ever break you decrypted data, even when using a non-fixed block size, but it is *possible* when you are encrypting binary data.
shadowhand
A: 

In my implementation of TripleDES, I found the decrypted string was padded with \5 or \6 characters. This wasn't the expected \0 or \4 characters mentioned above or in the PHP.net examples. To determine the ASCII value of the padding character use the ord() function. ord() works on a single character so use str_split() to break up a string or access the character directly with array notation - $string[5].

Final trim result - trim($decrypt, "\0..\32");

Final code result -

    $key        = "encryption key";
    $encrypt    = base64_decode($encrypt);
    $iv_size    = mcrypt_get_iv_size(MCRYPT_3DES, MCRYPT_MODE_ECB);
    $iv         = mcrypt_create_iv($iv_size, MCRYPT_RAND);
    $decrypt    = mcrypt_decrypt(MCRYPT_3DES, $key, $encrypt, MCRYPT_MODE_ECB, $iv);
    $final      = trim($decrypt, "\0..\32"); // removes potential null padding
JCotton