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) . "
";
$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.
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/
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.
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.
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.
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.
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
);
In order to decrypt your friend will need to know:
MCRYPT_RIJNDAEL_128
MCRYPT_MODE_CFB
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.
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.