mcrypt_decrypt adding null at the end of decrypted text - php

I used Rijndael algorithm to encrypt and decrypt my database password. I kept encoded password in another file. Here I reduced the code to get relevant :
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, PASSWORD_SALT, 'mypassword', MCRYPT_MODE_ECB);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, PASSWORD_SALT, $encrypted, MCRYPT_MODE_ECB);
// !! The value of $decrypted is "mypassword " i.e. "mypasswordNULLNULLNULLNULLNULL"
'mypassword' is converted to 'mypassword' + 6xNULL. The decrypted code is containing null.
I wrote this code 1 year ago and everything was working fine. But now, when version of all technologies have changed, I am having problem.

It was always so.
According to documentation:
The data that will be decrypted with the given cipher and mode. If the size of the data is not n * blocksize, the data will be padded with '\0'.
So either you trim your data with \0, or you have to store the original length anywhere and then cut the padded 0 off.

Using the Rijndael-128 algorithm mcrypt_encrypt() will always return multiples of 16 bytes. If your plain text ist not an exact multiple of 16 bytes the data will be padded with zero bytes so it will be a multiple of 16.
Those zero Bytes will appear in the decrypted text as well. You have to remove those by using:
$decrypted = rtrim($decrypted, "\0");
Note 1: Rijndael is a block encryption algorithm which operates on block of a fixed size. This is why padding may be necessary.
Note 2: Encryption is only suitable for encoded input that never ends with value 00h (because of default zero padding). Taken from example code at http://php.net/manual/en/function.mcrypt-encrypt.php

Related

What is the length I must specify for string encrypted with AES-256-CBC in the database?

I am using AES 256 cbc method to encrypt my files.
The column which I am encrypting is called 'Name'.
previously before encrypting I had set the varchar length in phpmyadmin for 'Name' to be 20. when I was trying to encrypt , I saw it was short and the entire encrypted string was not getting inserted in the database.
So I changed the size of varchar to 50 but still the length is small.
I have to do this for other column as well. How do I determine efficient length for 'Name' column.
I am using randomized IV in the encryption as can be seen from the below example.
$encryptionMethod = "AES-256-CBC";
$secretHash = "25c6c7ff35b9979b151f2136cd13b0ff";
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($encryptionMethod));
//To encrypt
$encrypted = openssl_encrypt($textToEncrypt, $encryptionMethod, $secretHash,false,$iv);
$encryptedMessage = $encrypted . ':' .base64_encode($iv);
during decryption I use
$parts = explode(':', $encryptedMessage);
// Decrypt the data
$decryptedMessage = openssl_decrypt($parts[0], $encryptionMethod, $secretHash, 0, base64_decode($parts[1]));
echo $decryptedMessage;
since the IV is appended to the encrypted string , how would I be able to calculate the length needed to be defined in the database for the column 'Name'.
The block size of AES is 16 bytes, so you you'll need
the size of your input, rounded up to the closest multiple of 16
plus, if the input is already a multiple of 16, one block size for the PKCS#5 padding
plus 16 bytes for the IV
Note that this doesn't necessarily apply to other cipher modes1.
So for 20 bytes of input you'll need a total of 48 bytes. However, you are also base64 encoding the result, which requires at least 33% more space (i.e. you should be storing the raw bytes if you care about space).
You should always concatenate before encoding, otherwise you often waste space with multiple padding byte sequences. If your input is 20 bytes long, encoding the 32 byte ciphertext by itself produces 44 bytes, and encoding the IV produces 24 bytes (both need padding). Concatenating before encoding produces only 64 bytes.
Concatenating before encoding also doesn't require the delimiter, because the length of the IV is known.
1 AEAD ciphers, such as GCM, are generally preferable over CBC, but require more space for storing the authentication hash, obviously.

Encrypt data with aes-256-cbc with openssl in php

I am trying to encrypt hexadecimal data with openssl aes-256-cbc in php language. My data length is 32 byte and key length is 32 byte and iv is 16 byte 0.
I used openssl_encrypt function to encrypt the hexadecimal data but function output so long I expected. I don't know where is my fault.
Here is the code below:
$plainData = "00A40800063D103D202F2400";
$encryptionMethod = "aes-256-cbc";
$encryptionKey = "395f426c0e5bd914375837483b791d80854dd9a19dd86fd189e94ccade60c5b8";
$iv = "0000000000000000";
$encryptedData = openssl_encrypt($plainData, $encryptionMethod, $encryptionKey, $iv);
Output: 6e6469763877536e534a4f677168716f67692f684a4166315767534951764f645a575044554f6f4162763333332f6c516d6a397635723566713259444f6e79586137586e366e4f476e7a46765a4b45302b4b4855676961786757556361373932766869584453385749726f3d
Expected output length is 32 byte but above output is 216 character string.
Where is the problem above code block?
The length is different in size because of so called padding. The padding used by default is PKCS#7 compatible padding. This padding is always added; this way the padding can be distinguished from possible (binary) plaintext.
If you already know that the ciphertext is always a multiple of the block size then you can use OPENSSL_NO_PADDING as option both during encryption and decryption; you will then be responsible for making sure that the plaintext is a multiple of the block size during encryption.
It is possible to use AES-CBC with an all zero IV, but remember that this leaks information about the plaintext: repeated plaintext blocks at the start of the plaintext message can be identified. The only way to reliably use an all zero IV is to make sure that the first plaintext block is never identical or to change the key on every call.
Also note that CBC mode is malleable - if the ciphertext or plaintext can be altered by an attacker the simple encryption mode you're describing is bound to have security issues.

Common PHP mcrypt_encrypt() code sometimes fails decryption

This code is from several websites, the source of which has mentioned SO as a source for help in its creation.
This is the uncompressed version, of which I changed the base64 encode and decode to hex2bin and bin2hex as I thought this was giving me problems when passing between webpages, but was because I was on occations getting trash data as the decryption result.
With either code, using the same key and different data, on occasions gives me trash data as the decryption result. It seems to be the data itself, as opposed to repetition, the data size may be between 30 - 80 characters.
The data is just a series of numbers and letters, all within the usual ASCII standard, no nulls, nothing beyond standard email address characters.
I'm using Ubuntu 14.04 LTS, with PHP 5.5.9-1ubuntu4.5 (Zend: 2.5.0)
Has anyone experienced this kind of problem?
EDIT: From reading more SO, apparently $iv should be the same for encrypt and at the time of decrypt, but if this is true why would it work most of the time anyway?
function mc_encrypt($encrypt, $mc_key) {
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
$passcrypt = trim(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $mc_key, trim($encrypt), MCRYPT_MODE_ECB, $iv));
$encode = bin2hex($passcrypt);
return $encode;
}
function mc_decrypt($decrypt, $mc_key) {
$decoded = hex2bin($decrypt);
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND);
$decrypted = trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $mc_key, trim($decoded), MCRYPT_MODE_ECB, $iv));
return $decrypted;
}
Rijndael is a symmetric block cipher. It operates on blocks and in this case, the blocks are 256-bit wide. The ciphertext is supposed to look random, but it isn't. Otherwise, it wouldn't be possible to reverse the encryption. Since this is ECB mode, there is no IV which randomizes the whole thing. And because of this every plaintext block combined with the key will result in the same ciphertext block. So the issue is reproducible only based on the same data.
Since the encryption is supposed to look random, it will happen that the first byte of the ciphertext will contain a whitespace (when shown as characters). trim removes 6 types of whitespace and will remove at least the first byte in those cases. So, the problem will happen in 6/256 of cases (~2%).
If for example the first byte is a space, then it will be removed. Now the first block doesn't have all the bytes and the block boundary for decryption is moved. So, the first byte from the next block is used as the last byte of the first block to decrypt the block. Since Rijndael is a block cipher, only garbage will come out. And this happens for all blocks.
Usually rtrim is used instead of trim to unpad the plaintext: rtrim(mcrypt_decrypt(...), '\0');. The trim of the ciphertext should be removed completely.
This isn't whole story, because the end of the encryption/decryption is trimmed which can break the result. It's best to actually use PKCS#7 padding by implementing it yourself and completely lose the *trim. Sadly php_mcrypt doesn't provide the PKCS#7/PKCS#5 padding.

Extra Characters Output From mcrypt_decrypt

I am in need of symmetric encryption and decryption in a pair of PHP scripts. I am using mcrypt_encrypt and crypt_decrypt. To test this, I have the following code:
$encrypted_token = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $ENCRYPTION_SECRET, $refresh_token, MCRYPT_MODE_ECB);
$encrypted_encoded_token=base64_encode($encrypted_token);
echo "\nEncrypted Token: " . $encrypted_encoded_token . "\n";
To test this, in the same PHP script I do the following:
$decoded_refresh_token = base64_decode($encrypted_encoded_token);
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $ENCRYPTION_SECRET, $decoded_refresh_token, MCRYPT_MODE_ECB);
echo "\nDecrypted Token After decrypt: " . $decrypted . "\n";
My input $refresh_token is a long string of characters that looks something like this (only longer):
AQCNKTZhaWTmUl3WvHOtlkv2Vc-Ybkl24D5Zp1lZPLBraTxrr-YQhErXrI2IWWEIWk5lnBc1k
The Decrypted Token After decrypt looks something like this:
AQCNKTZhaWTmUl3WvHOtlkv2Vc-Ybkl24D5Zp1lZPLBraTxrr-YQhErXrI2IWWEIWk5lnBc1k�������������������
My $ENCRYPTION_SECRET is 32 characters long. The base64_encode and decode are because I am json_encoding and decoding the token in a Post.
What am I doing wrong?
Those extra characters are indeed bytes valued zero. PHP's mcrypt uses 0..n - 1 bytes of zero padding, where n is the blocksize. In other words, if the plaintext is already a multiple of the blocksize then it doesn't pad. Otherwise it pads up to the block size.
Now you are using MCRYPT_RIJNDAEL_256 which is not AES, but Rijndael with a block size of 256 bits. So the number of bytes added are 0..31 bytes. If you view those as a string they get converted to question marks or removed, depending on what you view the string with. In the C-library of mcrypt that probably made more sense as zero terminates a null-terminated string.
Nowadays the ad-hoc standard is PKCS#7 padding, which adds 1..blocksize of padding bytes. If x is the number of padding bytes then x is also the value of the bytes added. PKCS#7 padding is deterministic, i.e. you can always unpad, no matter the value of the plaintext. Zero padding behaves largely the same way, unless the plaintext contains zero characters at the end. This is however never the case for printable strings (in ASCII, latin or UTF-8).
Finally, to remove the padding, simply perform rtrim(plaintext, "\0") and you'll get the original string.

How to avoid that "%2500" gets added to string after encryption? [duplicate]

Working with OAuth and encrypting the keys with the following function with a string which we'll call 'foo' (actually an OAuth token)
public function encrypt( $text )
{
// add end of text delimiter
$data = mcrypt_encrypt( MCRYPT_RIJNDAEL_128, $this->key, $text, MCRYPT_MODE_ECB, $this->iv );
return base64_encode( $data );
}
When I decrypt it using the inverse function, I end up with:
Function:
public function decrypt( $text )
{
$text = base64_decode( $text );
return mcrypt_decrypt( MCRYPT_RIJNDAEL_128, $this->key, $text, MCRYPT_MODE_ECB, $this->iv );
}
Result:
foo%00%00%00%00%00%00%00%00%00%00%00%00%00%00
Edit:
Looking at it a little more, I realized that it is actually URL encoding to %00, which means that my strings are somehow being padded by null characters? So I am currently using trim() to get rid of them, but I would like to understand why this is happening.
Rijndael is a block cypher, which means that it operates on chunks of data of a particular length (128 bits in this case). This means that if the length of the input text is not a multiple of the block size, it must be padded out to fit. In this case, the padding is zeros; there are a number of other possible padding schemes that could be used, but if you want them with PHP's mcrypt you'll have to apply them manually.
You can Fix it with this method to get rid of padding characters: In our case we are using Zend.
$filter = new Zend_Filter_Decrypt(array('adapter' => 'mcrypt'));
$filter->setVector($lpt->_seed);
str_replace("\x0", '', trim($filter->filter(base64_decode($textToDecrypt))));
MCRYPT_MODE_ECB means that you are using ECB, a block cipher mode of operation. Block ciphers can be handled either for block cipher modes of operation or for stream cipher modes of operation. Common block cipher modes are ECB and CBC, a common stream cipher mode is CTR, better known as counter mode operation.
MCRYPT_RIJNDAEL_128 is an implementation of AES. AES is the Rijndael cipher with a block size of 128 bits, and three possible key sizes, 128, 192 and 256 bits. So if you use a block cipher mode of encryption then you need to divide up the plain text is sizes of 128 bits - 16 bytes - each. Of course this leaves you with the question what to do when the last block is not 16 bytes.
PHP's mcrypt_encrypt more or less leaves this up to the user. It pads with 00 valued characters if the block is not full up to the block size. This is fine if the input is a string; you can simply trim off the 00 characters from the returned string. If the input is however binary data that ends with a 00 character than that character is lost (+ any other character that is taken from the start and end of the string of course). You can also send the length of the string encrypted together with the plaintext of course.
For a better scheme you only have to look at PKCS#7 padding. Multiple code snippets for implementing the padding can be found in the comments section of mcrypt_encrypt.
mcrypt_encrypt currently does not seem to support stream modes for AES, so that option is out if you want to keep with the PHP mcrypt library.

Categories