Picking encryption cipher for mcrypt - php

I have few questions about this code:
<?php
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = "This is a very secret key";
$text = file_get_contents('path/to/your/file');
echo strlen($text) . "\n";
$crypttext = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $text, MCRYPT_MODE_ECB, $iv);
file_put_contents('path/to/your/file', $crypttext);
?>
It encrypts the file just fine, however it adds additional nulls at the end, so if I encrypt:
a test string is this one
and here is a new line
once decrypted becomes:
a test string is this one
and here is a new line 000000000000000
What's going on?
Second, is MCRYPT_RIJNDAEL_256 compatible with AES-128?
Finally, how would I let another party decrypt a file I've encrypted? They would need to know which encryption was used and I am not sure what to tell them.

Much of what I'm about to explain can be gleaned from #ircmaxwell's excellent slidedeck: Cryptography For The Average Developer which you should check out immediately. I'll reiterate one of his main points: Avoid writing code that deals with encryption/decryption. Unless you understand all the factors you will probably flub it.
MCRYPT_RIJNDAEL_128 closely matches AES
The Advanced Encryption Standard was created by The National Institute of Standards and Technology. NIST chose the Rijandael cipher from a competitive pool of cryptographic experts.
Common confusion: the numbers in AES and Mycrpt refer to different things
AES-128 refers to 128 bit key size
AES-256 refers to 256 bit key size
MCRYPT_RIJNDAEL_128 refers to a 128 bit cipher block size
MCRYPT_RIJNDAEL_256 refers to a 256 bit cipher block size
Key size and block size
Key Size refers to the length of the secret used to encrypt/decrypt. A 256 bit key is 32 bytes, roughly 32 characters.
Block Size is a property of block ciphers (i.e. all AES candidates) which chunk out the data to a specific size during the cipher process.
AES specifies a 128 bit cipher block
Mcrypt's Rijndaels all accept 256 bit keys
based on key/block size MCRYPT_RIJNDAEL_128 seems to conform closest to AES-256
there are other factors to consider — like how many transformation rounds occur? — it's hard to verify this aspect without digging into the source code
Mode of Operation
The mode is an important element.
Both ECB and CBC mode pad your plaintext into the block size. If you're encrypting 1 byte of data with a 128 bit block size you will get 15 null bytes. Padding opens up the possibility for a padding oracle attack.
Beyond padding ECB does not make use of an Initialization Vector (IV) which leaves you open to a potential vulnerability: prefer CBC or CFB mode.
You can avoid both problems by using CFB mode which does not need padding and therefore does not require post-decryption trimming.
Initialization Vector (IV)
When creating your IV you need a crypto-strong random source: MCRYPT_RAND is not random enough — MCRYPT_DEV_RANDOM or MCRYPT_DEV_URANDOM are preferred.
$iv_size = mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_128,
MCRYPT_MODE_CFB
);
$iv = mcrypt_create_iv(
$iv_size,
MCRYPT_DEV_URANDOM
);
Finally, for your friends
In order to decrypt your friend will need to know:
the cipher — MCRYPT_RIJNDAEL_128
the block mode — MCRYPT_MODE_CFB
the IV — each element you encrypt should get a unique IV, so you'll want to append the IV to the encrypted data
the key — this is secret, and should stay the same for all encryptions; if your key gets compromised rotate it out
Only the key needs to stay secret. Depending on your transport method you should consider implementing a data integrity check to ensure the ciphertext data wasn't tampered with. Again, see #ircmaxwell's excellent slidedeck: Cryptography For The Average Developer for an example of creating an HMAC fingerprint using hash_hmac().
It should be obvious that the maintenance all of these moving parts is rife with complexity. Tread carefully.
Other ciphers
Mcrypt gives you other cipher options. Some, like DES, are not recommended. Others were candidates for AES, like Blowfish, TwoFish, and Serpent. Rijandael is a vetted, proven cipher, and is recommended.

MCRYPT_RIJNDAEL_128 is AES-128, MCRYPT_RIJNDAEL_256 is AES-256 - just another name:
[...]The standard comprises three block
ciphers, AES-128, AES-192 and AES-256,
adopted from a larger collection
originally published as Rijndael.originally published as Rijndael.[...]
[...]The Rijndael cipher was developed by
two Belgian cryptographers, Joan
Daemen and Vincent Rijmen, and
submitted by them to the AES selection
process. Rijndael (pronounced "Rhine
dall") is a wordplay with the names of
the two inventors.[...]
The \x00 characters you encounter at the end of the decrypted string are the padding required for some block ciphers (with ECB being such a block cipher). Mcyrpt uses NULL-padding internally if the input data needs to be padded to the required block length. There are other padding modes available (which have to be user-coded when using Mcyrpt), namely PKCS7, ANSI X.923 or ISO 10126. NULL-padding is problematic when encrypting binary data that may end with one or more \x00 characters because you can't detect where the data ends and the padding starts - the other padding modes mentioned solve this kind of problem. If you're encrypting character data (strings) you can easily trim off the trailing \x00 by using $data = trim($data, "\x00");.
To decrypt the data you sent to a consumer, the consumer would need to know the IV (initialization vector) ($iv), the algorithm used (MCRYPT_RIJNDAEL_256/AES-256), the encryption mode (ECB), the secret encryption key ($key) and the padding mode used (NULL-padding). The IV can be transmitted with the encrypted data as it does not need to be kept secret:
The IV must be known to the recipient
of the encrypted information to be
able to decrypt it. This can be
ensured in a number of ways: by
transmitting the IV along with the
ciphertext, by agreeing on it
beforehand during the key exchange or
the handshake, by calculating it
(usually incrementally), or by
measuring such parameters as current
time (used in hardware authentication
tokens such as RSA SecurID, VASCO
Digipass, etc.), IDs such as sender's
and/or recipient's address or ID, file
ID, the packet, sector or cluster
number, etc. A number of variables can
be combined or hashed together,
depending on the protocol.depending on the protocol.

MCRYPT_RIJNDAEL_128 is equivalent to AES-128 in mcrypt. This is documented under MCRYPT_RIJNDAEL_128 here: http://mcrypt.sourceforge.net/

Related

About RC4 Decryption PHP (mcrypt)

I'm looking for anything about RC4 Decryption with decode the input using: Hexa
Lucky for me, I found
PHP's mcrypt_encrypt.
I want to decrypt many cipher files with the same key.
But, I had a problem with:
$iv_size = mcrypt_get_iv_size(MCRYPT_ARCFOUR, MCRYPT_MODE_STREAM);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
print (mcrypt_decrypt(MCRYPT_ARCFOUR, $key, $text, MCRYPT_MODE_STREAM, $iv));
(And UTF-8 Vietnamese)
The result of echo $iv_size is 0.
Please help me, I don't know how I can fix it?
Key : Lyr1cjust4nct (key file .txt)
Mode: STREAM
Decode the input using: Hexa
Ciphertext: cipher.txt (Hexa)
http://pastebin.com/bmYcmU0J
RC4 doesn't support IVs. You instead need to use a unique key for each message.
RC4 has two big weaknesses that apply to your situation:
Using related keys is not secure. So you can't just concatenate a fixed key with a variable/unique IV. You'd need to use some kind of hashing scheme.
The beginning of the output is very biased, which leaks information about the ciphertext. So you need to throw away the beginning of the key-stream. I think throwing away 1024 bytes should take care of the biggest biases.
RC4 doesn't include any integrity protection (MAC). So if an attacker manipulates the ciphertext, you'll run into problems.
=> Don't use RC4. Use AES in an authenticated mode such as GCM or by combining AES with a MAC using the encrypt-then-MAC principle.
I strongly recommend using a high level library written by experts, since people get encryption wrong very often, even when using standard primitives like AES.

Decrypt from AES-ECB ciphertext created with php/mcrypt using node.js

I have an encrypted message using MCRYPT_RIJNDAEL_128, ECB mode, and Base64, I've tried decrypting using crypto(https://www.npmjs.com/package/crypto) with the following code:
var text = '';
var decipher = crypto.createDecipheriv("aes-128-ecb","SOMEKEYHEREWITHLENGTH32ooooooooo", '');
text += decipher.update(data, "base64");
text += decipher.final();
The error I've been getting is: Invalid key length 32.
What should the length of key be? When I attempt to use 16 instead the error thrown is:
'TypeError: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt'
If client requires to use ECB with key of length 32, how can I make this case valid?
AES is a subset of Rijndael with a fixed block size 128 bit whereas Rijndael supports block sizes of 128, 192 and 256 bit. The 128 in MCRYPT_RIJNDAEL_128 means the block size. The 128 in aes-128-ecb on the other hand means the key size. Both AES and Rijndael support key sizes of 128, 192 and 256 bit.
If you have a 32 character key and this key is not hex-encoded to reach those 32 characters then it is possible that you actually want aes-256-ecb.
Keep in mind that the key encoding might be different than the one in PHP so might need to parse the key into a Buffer with a specific encoding.
Also, please don't use ECB mode. It is insecure. You should use at least CBC mode with a random IV. Since the IV does not need to be secret, you can simply send it along with the ciphertext as a prefix for example. You would then need to slice it off before decryption.

AES 256 and Base64 Encrypted string works on iOS 8 but truncated on iOS 7

One of my app needs to download a database with the content encrypted in AES 256. So I've used on server side phpAES to encode the strings in AES CBC with an IV.
On the iOS side I'm using FBEncryptor to decrypt the string.
This is the code on the server side:
$aes = new AES($key, "CBC", $IV);
$crypt = $aes->encrypt($string);
$b64_crypt = base64_encode($crypt);
On the iOS side I'm doing this:
NSString* decrypt = [FBEncryptorAES decryptBase64String:b64_crypt keyString:key iv:iv];
Actually everythings works fine on iOS 8. The problem is on iOS 7 where the decoded string is truncated at random length.
Thoughts?
Don't use phpAES. You're shooting yourself in the foot with an enormous cannon.
From their page:
The free version only supports ECB mode, and is useful for encrypting/decrypting credit card numbers.
This is incredibly wrong and misleading. ECB mode is not suitable for any purpose except as a building block for other modes of operation. You want an AEAD mode; or, failing that, CBC or CTR with HMAC-SHA2 and a CSPRNG-derived IV/nonce. Using unauthenticated encryption is a very bad idea.
For interoperability with iOS, you should use libsodium.
Objective-C: SodiumObjc or NAChloride
PHP: libsodium-php (also available in PECL)
If you cannot use libsodium, your best bet is OpenSSL and explicitly not mcrypt, and a compatible interface on the iOS side.
All currently supported versions (5.4+) of PHP expose openssl_encrypt() and openssl_decrypt() which allow fast and secure AES-CBC and AES-CTR encryption. However, you should consider using a library that implements these functions for you instead of writing them yourself.
The truncation could be the result of incompatible padding.
phpAES uses non-standard null padding similar to mcrypt, this is unfortunate since the standard for padding is PKCS#7. It is unfortunate that one has to read the code to find that out. It is important to supply a 256-bit (32-byte) key since that sets the key size for the algorithm.
FBEncryptor only supports PKCS#7 padding.
So, these two methods are incompatible.
One solution is to add PKCS#7 padding to the string in php prior to calling phpAES which will not then add the null padding. Then FBEncryptor will be compatible with the encrypted data.
PKCS#7 padding always adds padding. The padding is a series by bytes with the value of the number of padding bytes added. The length of the padding is the block_size - (length(data) % block_size.
For AES where the block is is 16-bytes (and hoping the php is valid, it had been a while):
$pad_count = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($pad_count), $pad_count);
Please add to the question working example keys, iv clear data and encrypted data as hex dumps.

How can I decrypt data encrypted with 'rijndael-128-cbc' in ruby

I need to decrypt data which is encrypted by php with 'rijndael-128-cbc' algorithm.
Now I have a problem to convert php code to ruby code.
ruby OpenSSL::Cipher doesn't support rijndael-128-cbc then I use "aes-128-cbc".
I heard AES is based on the Rijndael cipher, so I guess I can convert decryption with rijndael-128-cbc' to AES-128-CBC.
enc = OpenSSL::Cipher.new "AES-128-CBC"
enc.encrypt
puts enc.key_len
It's output is 16
However, In php
echo mcrypt_get_key_size('rijndael-128', 'cbc')
this gets 32
Is there any difference between 'rijndael-128' and "AES-128-CBC"?
and How can I convert rijndael-128-cbc descrytion to ruby?
I am no Ruby developer, so I can't help you with the ruby code, but I can give you a few pointers about Rijndael, and PHP's mcrypt extension ...
Yes, AES is based on Rijndael-128 and therefore Rijndael-128 and AES-128 are the same thing.
It's important to note that the number in Rijndael's variations refers to the cipher's block size, while for AES - it's the key size, so (for example) Rijndael-192 is NOT AES-192; AES always has a block size of 16 bytes or 128 bits.
That being said, Rijndael-128 can also be AES-192 and AES-256, the difference being only the key size.
As described in the PHP manual for mcrypt_get_key_size(), the function returns the maximum key length for the supplied cipher ...
AES-256 == Rijndael-128 with a 32-byte key
This is why it's returning 32. For AES-128, the key size is of course 16 - you can hardcode that.
Hopefully, that will clear it up a bit for you.

PHP (mcrypt_encrypt) Ruby (AES-256-CBC) Encryption Different Results

I have been trying to encrypt a string in PHP and Ruby using the same key and iv but I always got different results.
Below is the PHP Code
$data = "This string needs to be encrypted";
$key = "1234567887654321abcdefghabcdefgh";
$iv = "1234567887654321abcdefghabcdefgh";
echo $encrypted_data = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $data, MCRYPT_MODE_CBC, $iv);
Below is the Ruby Code
data = "This string needs to be encrypted"
key = "1234567887654321abcdefghabcdefgh"
iv = "1234567887654321abcdefghabcdefgh"
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
aes.encrypt
aes.key = key
aes.iv = iv
encrypted_data = aes.update(data) + aes.final
Could somebody please help me get the same encrypted data in PHP and Ruby? I encrypted some data in PHP and then decrypted in Ruby but didn't get the data back. So I think the issue is PHP and Ruby encryption and decryption mechanism work differently. Please correct me if I am wrong. Thanks
Don't hard code IV's , it is insecure. IVs must be random but can be public , so just use
mcrypt_create_iv and prepend it to the front of the ciphtertext and then extract it before
decrypting
You likely have three problems
MCRYPT_RIJNDAEL_256 is nott AES. AES is a specific version RIJNDAEL that was standardized with a 128 bit block size and either 128 or 256 bit keys. MCRYPT_RIJNDAEL_256 is RIJNDAEL with a 256 bit block size. You want to use MCRYPT_RIJNDAEL_128 which is actually AES. For php, key length is just determined by the length of the key. So just give it a 256 bit ( 32 character) key and you will be fine. Note block size does not effect security really, so don't worry about the deference and just use AES with a 256 bit key: its good enough for the NSA and top secret data.
Padding. AES only takes fixed 128 bit chunks, so you must pad out the text to be a multiple of that size. PHP doesn't really bad and i believe SSL uses pkcs7 padding. Note that even with different padding schemes, for most of them the start of the cipher text should the the same there just may be garbage at the end.
String encoding. AES is defined with bit inputs, in c typically this is a byte array. Ruby and PHP use strings. I'd be willing to bet your string encodings are different.

Categories