Problem with serialize/unserialize and mcrypt when doing Signed Cookies - php

I'm working with a friend to get signed cookies on a website but I'm having a lot of problems when trying to encrypt it with mcrypt and MCRYPT_RIJNDAEL_256. I've the cookies working fine, so the problem is only when encrypting/decrypting the value of the cookie.
Here is the error is showed when trying to decrypt the cookie:
Notice: unserialize(): Error at offset 0 of 93 bytes in /var/samba/www/xxx/src/data/include/yyy/Cookie.php on line 94
This exact line corresponds to:
$this->_cookie["value"] = unserialize(mdecrypt_generic($tv, $cookie_value));
And here is how I manage to encrypt / decrypt.
First, send cookie.
$tv = mcrypt_module_open(MCRYPT_RIJNDAEL_256, null, "ctr", null);
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($tv), MCRYPT_RAND);
mcrypt_generic_init($tv, "t3stp4ssw0rd", $iv);
$this->_cookie["value"] = base64_encode(mcrypt_generic($tv, serialize($this->_cookie["value"])));
mcrypt_generic_deinit($tv);
mcrypt_module_close($tv);
setrawcookie($this->_cookie["name"],
$this->_cookie["value"],
$this->_cookie["expire"],
$this->_cookie["path"],
$this->_cookie["domain"],
$this->_cookie["secure"],
$this->_cookie["httponly"]);
PD: Yep, lovely test password ;-)
The value I see on firebug for the cookie is:
oKWdbVLX9T+mbOut4swo/aXr0g5O/3ApqfWZ1GZlrwwMSTa+M4n8Uey0UQs827HB7tilc/OzUPWQxoNvnAIkP5CFGkvgn+j+I36qN6dB0HmOUPlkNXJlz8Tfqxrjf8Gx
My get cookie, where I've to decrypt the value is:
$this->_cookie["name"] = $cookie_name;
$this->_cookie["value"] = $_COOKIE[$cookie_name];
$cookie_value = base64_decode($this->_cookie["value"]);
$tv = mcrypt_module_open(MCRYPT_RIJNDAEL_256, null, "ctr", null);
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($tv), MCRYPT_RAND);
mcrypt_generic_init($tv, "t3stp4ssw0rd", $iv);
$this->_cookie["value"] = unserialize(mdecrypt_generic($tv, $cookie_value));
mcrypt_generic_deinit($tv);
mcrypt_module_close($tv);
return $_COOKIE[$cookie_name];
The problem is when I try to unserialize the value of the decrypted data. Anyone know where can be the problem?
Thank you in advance!
UPDATE:
$cookie_value = base64_decode($this->_cookie["value"]);
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, "", "cfb", "");
$ks = mcrypt_enc_get_key_size($td);
$key = substr(sha1("t3stp4ssw0rd"), 0, $ks);
$ivs = mcrypt_enc_get_iv_size($td);
$iv = substr($cookie_value, 0, $ivs);
$cookie_value = substr($cookie_value, $ivs);
mcrypt_generic_init($td, $key, $iv);
$cookie_value = mdecrypt_generic($td, $cookie_value);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
$this->_cookie["value"] = unserialize($cookie_value);
Returns me error Warning: mcrypt_generic_init(): Iv size incorrect; supplied length: 0, needed: 32

That offset error normally means that the length of one of the values doesn't correspond to the specified length denoted by the serialized data. In my experience, this normally comes down to:
as the previous poster said, backslashes being inserted to escape characters
Encoding issues. This normally arises where some characters are computed as having one byte when serialized, but when unserializing they suddenly have 2 bytes. This can happen when you have, say, an ISO-8859-1 character set, but then some operation changes it to UTF-8.
If I were to take a guess, I'd say that the second point is probably where your problem lies. Your procedure in the first instance is serialize->encrypt->base64_encode, then you reverse the sequence, but I suspect that, somewhere along the line, your character encoding is getting messed up.
Edited: Okay, I took a look at your code, and there's a problem with your encryption/decryption. Your decryption is not returning the decrypted value back. A while ago I came across this function (wish I could remember where so I can attribute it correctly) which is what I use for Mcrypt. It works for both encode and decode. Try it, and see if it sorts out your problem (the only thing it doesn't do is your base64_encode). I think your problem was that you were missing some required steps.
function encDec( $data, $key, $encrypt=true, $cypher='rijndael-128') {
if (function_exists('mcrypt_module_open')) {
# Serialize, if encrypting
if ( $encrypt ) { $data = serialize($data); }
# Open cipher module
if ( ! $td = mcrypt_module_open($cypher, '', 'cfb', '') )
return false;
$ks = mcrypt_enc_get_key_size($td); # Required key size
$key = substr(sha1($key), 0, $ks); # Harden / adjust length
$ivs = mcrypt_enc_get_iv_size($td); # IV size
$iv = $encrypt ?
mcrypt_create_iv($ivs, MCRYPT_RAND) : # Create IV, if encrypting
substr($data, 0, $ivs); # Extract IV, if decrypting
# Extract data, if decrypting
if ( ! $encrypt ) $data = substr($data, $ivs);
if ( mcrypt_generic_init($td, $key, $iv) !== 0 ) # Initialize buffers
return false;
$data = $encrypt ?
mcrypt_generic($td, $data) : # Perform encryption
mdecrypt_generic($td, $data); # Perform decryption
if ( $encrypt ) $data = $iv . $data; # Prepend IV, if encrypting
mcrypt_generic_deinit($td); # Clear buffers
mcrypt_module_close($td); # Close cipher module
# Unserialize, if decrypting
if ( ! $encrypt ) $data = unserialize($data);
}
return $data;
}

Make sure the quotes " in the cookie information are not escaped using back slashes. If they quotes are escaped, remove them before encrypting. See this thread -> PHP unserialize error at offset, works on some servers, not others

Related

mcrypt_decrypt wrong output after using encrypted data php

I have a small problem with php mcrypt_decrypt function. Firstly, I use a 16-byte string, and encrypt it using mcrypt_encrypt; then, I use base64_encode, and put the output to mcrypt_decrypt, in order to get the initial string.
But the output is not what's expected. I checked that my base64 decoded string input for decoding is the exact output produced by mcrypt_decrypt. Here is my code:
//encrypt
$str="KKQT9W4st7vmdkps";
$key="43625A8C1E4330BDF84DDEE3DD105037";
$block = mcrypt_get_block_size('rijndael_128', 'ecb');
$passcrypt=mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $str, MCRYPT_MODE_ECB);
echo $passcrypt;
That outputs PTfZ6Ephh8LTxXL4In33Og==. The decryption script is the following:
//decrypt
$str='PTfZ6Ephh8LTxXL4In33Og==';
$key='43625A8C1E4330BDF84DDEE3DD105037';
$str = base64_decode($str);
$str = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key,
$str, MCRYPT_MODE_ECB,''),"\0");
$block = mcrypt_get_block_size('rijndael_128', 'ecb');
echo $str;
And the output is not KKQT9W4st7vmdkps, but -nγ kk7Ζn’T instead. Any ideas? I'm using XAMPP and Apache server.
Thx guys for the feedback it was a silly mistake that i made...actually 'PTfZ6Ephh8LTxXL4In33Og==' was wrong in the decrypt function cause "I" was "l" in the end...so the decryption was not correct...but it was not my fault either since I was getting this string from a QR CODE scanner and both "I" and "l" are displayed the same...
For encryption, you need to:
1) Create an encryption resource
$str = "KKQT9W4st7vmdkps";
$key = "43625A8C1E4330BDF84DDEE3DD105037";
$r = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '',MCRYPT_MODE_ECB, '');
2) Randomly create encryption vector based on the size of $r
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($r),MCRYPT_RAND);
3) Initiliazing module using the resource,key and string vector
mcrypt_generic_init($r,$key,$iv);
4) Encrypt data/string using resource $r
$encrypted = mcrypt_generic($r,$str);
5) Encode it using base64_encode
$encoded = base64_encode($encrypted);
if(!mcrypt_generic_deinit($r) || !mcrypt_module_close($r))
$encoded = false;
6) Echoing it
echo 'Encrypted: '.$encoded;
For decryption, it's like a reverse process of encrypt
//Using the same enrypted string
$decoded = (string) base64_decode(trim($encoded));
$td = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '',MCRYPT_MODE_ECB, '');
$ivs = mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
mcrypt_generic_init($td,$key, $ivs);
$decoded = (string) trim(mdecrypt_generic($td, $decoded));
if(!mcrypt_generic_deinit($td) || !mcrypt_module_close($td))
$decoded = false;
Echoing it
echo 'Decrypted: '. $decoded;
Hope this helps. More info here.

3des in php - cannot get good key/string

I need to decode a 3des string in a php and I have no experience in decripting so far...
First step is: get the key and the set of strings to decode - I have that already.
I have this information about algorythm:
type: CBC,
padding - PKCS5,
initialization vector (iv?) - array of eight zeros
I try this way:
// very simple ASCII key and IV
$key = "passwordDR0wSS#P6660juht";
$iv = "password";
//$iv = array('0','0','0','0','0','0','0','0');
//$iv = "00000000";
$cipher = mcrypt_module_open(MCRYPT_3DES, '', 'cbc', '');
//$iv = mcrypt_enc_get_iv_size($cipher);
// DECRYPTING
echo "<b>String to decrypt:</b><br />51196a80db5c51b8523220383de600fd116a947e00500d6b9101ed820d29f198c705000791c07ecc1e090213c688a4c7a421eae9c534b5eff91794ee079b15ecb862a22581c246e15333179302a7664d4be2e2384dc49dace30eba36546793be<br /><br />";
echo "<b>Decrypted 3des string:</b><br /> ".SimpleTripleDesDecrypt('51196a80db5c51b8523220383de600fd116a947e00500d6b9101ed820d29f198c705000791c07ecc1e090213c688a4c7a421eae9c534b5eff91794ee079b15ecb862a22581c246e15333179302a7664d4be2e2384dc49dace30eba36546793be')."<br />";
function SimpleTripleDesDecrypt($buffer) {
global $key, $iv, $cipher;
mcrypt_generic_init($cipher, $key, $iv);
$result = rtrim(mdecrypt_generic($cipher, hex2bin($buffer)), "\0");
mcrypt_generic_deinit($cipher);
return $result;
}
function hex2bin($data)
{
$len = strlen($data);
return pack("H" . $len, $data);
}
At the beginnig you see example data, and on this data code works fine. Problem starts when I try to use my own data I get from database by SOAP webservice. I see this error:
Warning: pack() [function.pack]: Type H: illegal hex digit in....
I get this despite making attempts with different types of codings in the script. Script file itself is in ANCI.
Also: as you see in comments I also have made some experiments with IV but it doesn't make sense without dealing with first problem I gues.
Another thing is padding == PKCS5. Do I need to use it, and how should I do it in my case?
I would really appreciate help with this.
Ok, I have found a solution based mostly on this post: PHP Equivalent for Java Triple DES encryption/decryption - thanx guys and +1.
$iv = array('0','0','0','0','0','0','0','0');
echo #decryptText($temp->patient->firstName, $deszyfrator->return->rawDESKey, $iv);
echo #decryptText($temp->patient->surname, $deszyfrator->return->rawDESKey, $iv);
function decryptText($encryptText, $key, $iv) {
$cipherText = base64_decode($encryptText);
$res = mcrypt_decrypt("tripledes", $key, $cipherText, "cbc", $iv);
$resUnpadded = pkcs5_unpad($res);
return $resUnpadded;
}
function pkcs5_unpad($text)
{
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) return false;
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
return substr($text, 0, -1 * $pad);
}
However I get a warning (hidden with # now). "Iv should have the same lenght as block size" - I tried all combinations I could figure out and I can't get rid of it. Any idea people?
Edit: secondary problem fixed to. This kode will fix the iv:
$iv_size=mcrypt_get_iv_size("tripledes","cbc");
$iv = str_repeat("\0", $iv_size); //iv size for 3des is 8

DES/ECB/PKCS5Padding decryption in PHP

I'm in the need of decrypting with PHP (or Javascript) some service calls. I've spent all the day trying to accomplish, this, but I've been unable to decrypt it properly.
As a reference, the service provider sent me the following decryption sample code in Java:
DESKeySpec dks = new DESKeySpec("keyword".getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey key = keyFactory.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
SecureRandom sr = new SecureRandom();
cipher.init( Cipher.DECRYPT_MODE, key ,sr);
byte b[] = response.toByteArray();
byte decryptedData[] = cipher.doFinal( b );
I think I'm in the correct path by using:
$td = mcrypt_module_open(MCRYPT_DES, '', 'ecb', '');
$iv_size = mcrypt_enc_get_iv_size($td);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = substr($keyword, 0, mcrypt_enc_get_key_size($td));
mcrypt_generic_init($td, $key, $iv);
$decrypted = mdecrypt_generic($td, $data);
$decrypted = pkcs5_unpad($decrypted);
But, frankly, I'm sure I'm messing everything with the $iv creationg and $keyword setup (or maybe with $data or $decrypted types?). The pkcs5_unpad function is as follows:
function pkcs5_unpad($text)
{
$pad = ord($text{strlen($text)-1});
if ($pad > strlen($text)) return false;
return substr($text, 0, -1 * $pad);
}
I'm not only a noob on php, but also on cryptography techniques... could you please help me to solve this issue?
Make sure your key consists of the same bytes (strings may be encoded differently) and feed it a IV filled with zero's. ECB mode does not use an IV (and the PHP manual specifies as much), but if you do give it one default it to all zero's - the IV will be XOR'ed with the first plain text block, so setting it to all zero's will cancel out that operation. Also, make sure that the input cipher data is the same. Ignore the padding in the first instance, you should be able to check if the result is correct before unpadding.

AES decrypt in php

I am new to AES but from what I have found there are several modes (ECB,CBC, etc.) and different modes need different initialization vector requirements, blocks, and encodings. I am trying to decode the following
Xrb9YtT7cHUdpHYIvEWeJIAbkxWUtCNcjdzOMgyxJzU/vW9xHivdEDFKeszC93B6MMkhctR35e+YkmYI5ejMf5ofNxaiQcZbf3OBBsngfWUZxfvnrE2u1lD5+R6cn88vk4+mwEs3WoAht1CAkjr7P+fRIaCTckWLaF9ZAgo1/rvYA8EGDc+uXgWv9KvYpDDsCd1JStrD96IACN3DNuO28lVOsKrhcEWhDjAx+yh72wM=
using php and the (text) key "043j9fmd38jrr4dnej3FD11111111111" with mode CBC and an IV of all zeros. I am able to get it to work with this tool but can't get it in php. Here is the code I am using:
function decrypt_data($data, $iv, $key) {
$data = base64_decode($data);
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '', MCRYPT_MODE_CBC, '');
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
I think I may be missing something relating to base 64 encoding or turning the key into binary first. I have tried decoding many things and all I can produce is gibberish. Any help would be very appreciated.
Well the tool itself does not say how exactly it's encrypted. And you can't set the IV either so it's hard to get the parameters right (because they have to be equal).
After some guesswork I found out the following:
The IV is prepended to the ciphertext
The ciphertext is encrypted with aes-128-cbc
So you have to modify the code:
function decrypt_data($data, $iv, $key) {
$cypher = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
if(is_null($iv)) {
$ivlen = mcrypt_enc_get_iv_size($cypher);
$iv = substr($data, 0, $ivlen);
$data = substr($data, $ivlen);
}
// initialize encryption handle
if (mcrypt_generic_init($cypher, $key, $iv) != -1) {
// decrypt
$decrypted = mdecrypt_generic($cypher, $data);
// clean up
mcrypt_generic_deinit($cypher);
mcrypt_module_close($cypher);
return $decrypted;
}
return false;
}
$ctext = "Xrb9YtT7cHUdpHYIvEWeJIAbkxWUtCNcjdzOMgyxJzU/vW9x" .
"HivdEDFKeszC93B6MMkhctR35e+YkmYI5ejMf5ofNxaiQcZb" .
"f3OBBsngfWUZxfvnrE2u1lD5+R6cn88vk4+mwEs3WoAht1CA" .
"kjr7P+fRIaCTckWLaF9ZAgo1/rvYA8EGDc+uXgWv9KvYpDDs" .
"Cd1JStrD96IACN3DNuO28lVOsKrhcEWhDjAx+yh72wM=";
$key = "043j9fmd38jrr4dnej3FD11111111111";
$res = decrypt_data(base64_decode($ctext), null, $key);
I'm not sure why the key length is not used to encrypt it with aes-256-cbc - I've checked out the source of that as3crypto-library and it kind of supported it, but I would have to debug it to really verify it.

Encryption/Hashing issues

I'm working on a little script that will allow me to store relatively secure information in a cookie to validate a user login without the use of sessions. Part of the output is an encrypted salt to use when generating a hmac_hash with some of the information stored in the cookie, and some of the user information in the database.
However, after some testing, I've ran into a problem with the encryption/decryption of the strings and causing different hash results.
ie:
$str = '123456abcdef';
$hash1 = sha1($str);
$v1 = do_encrypt($str);
$v2 = do_decrypt($v1);
$hash2 = sha1($v2);
and I end up with
$hash1 - d4fbef92af33c1789d9130384a56737d181cc6df
$hash2 - 0d6034f417c2cfe1d60d263101dc0f8354a1216f
but when I echo both strings, they are both 123456abcdef.
The do_encrypt function is as follows:
function do_encrypt($value) {
$salt = generate_salt();
$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
mcrypt_generic_init($td, $ek, $salt);
$encrypted_data = mcrypt_generic($td, $value);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return base64_encode($salt.$encrypted_data);
}
The do_decrypt function:
function do_decrypt($value) {
$data = base64_decode($value);
$salt = substr($data, 0, 32);
$data = substr($data, 32, strlen($data));
$td = mcrypt_module_open('rijndael-256', '', 'cbc', '');
mcrypt_generic_init($td, $ek, $salt);
$decrypted_data = mdecrypt_generic($td, $data);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
return $decrypted_data;
}
for both functions $ek is an encryption key pulled from another file.
I'm trying to understand why the characters that display are the same, but the actual variables are different (otherwise the hash results would be the same), and is there any way to ensure that both strings are identical for hashing purposes?
Thanks,
Ryan.
As per comments, it looks like you are getting trailing nulls - It's likely that mcrypt has a block size of 32 bytes and that any encrypted/decrypted string must be a multiple of this many bytes.
Taken from the mcrypt_encrypt documentation:
If the size of the data is not n * blocksize, the data will be padded with '\0'.

Categories