I want to know if these encryption functions are ok for a live production application. Based on resources here and here and here I have put together these three functions.
Purpose
This code is for a encryption and decryption system that allows a user to encrypt certain data. The encrypted data is stored, while the keys are not stored at all.
The hash function is not used for verification but rather to add 'rounds', computational work to ensure that an attacker has to run many rounds whilst brute forcing through encryption keys. In addition the hash function extends the encryption key to the length of 32 bytes for use in the encryption functions.
I'm looking for advice regarding:
- Are the salts ok?
- Is this actually proper 256 bit encryption?
- Have I messed up any of my hash algo's etc.?
- These functions will be using encryption keys from the user side. Is there anything else I need to do that I am not doing?
- Are
encrypt256
,decrypt256
, andhashRound
suitable for production?
Here are some notes regarding my code:
- They are salted via the
mcrypt_create_iv
function. hashRound
is meant to slow down any brute force attacks.- Each round is actually 4 rounds, I guess the more hash functions the better right? In case one ever gets compromised.
- This is meant to be 256bit encrypted and this link suggests that you are to use
MCRYPT_RIJNDAEL_128
with a 256 bit key to attain that level of encryption, which is what the hash function spits out here. - This does confuse me a little bit, as the hash algo
ripemd128
actually produces 256bit - 32byte hashes, so why is it labelled 128?
Here is the algorithm:
- Start with the
EncryptionKey
andUserData
. - The user supplies both the
EncryptionKey
andUserData
. - To get (1) A 256 Bit key and (2) force a certain amount of computational work, hash the
EncryptionKey
X
rounds through aRoundsHash
function. This outputsNewEncryptionKey
. - Now encrypt the
UserData
withNewEncryptionKey
. - Save the
UserData
into the database.
I could skip the RoundsHash
and get the same result, encrypted and decrypted data.
However, as I want to extend the EncryptionKey
to precisely 32 bytes and also make any brute force attempt at decrypting the data slowly, I prefer to put the key first through the RoundsHash
function.
As I do not store either EncryptionKey
or NewEncryptionKey
, if the RoundsHash
function were to add a salt it would produce a different output (NewEncryptionKey
) and I would never be able to decrypt the data.
Remember that I am only using this hash function to extend the key and add computational work, I am not verifying a password hash as I never store anything other than the UserData
.
Here is the code listing:
function encrypt256($key, $input){
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($ivSize, MCRYPT_DEV_URANDOM);
$crypt = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $input, MCRYPT_MODE_CBC, $iv);
$encrypted = base64_encode($iv . $crypt);
return $encrypted;
}
function decrypt256($key, $input){
$ivSize = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$input = base64_decode($input);
$iv = substr($input, 0, $ivSize);
$crypt = substr($input, $ivSize, strlen($input));
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $crypt, MCRYPT_MODE_CBC, $iv);
return $decrypted;
}
function hashRound($rounds, $input){
for($x = 0; $x < $rounds; $x++){
$input = hash('sha512', hash('ripemd320', hash('whirlpool', $input)));
}
return hash('ripemd128', $input); // Sets the length of the key to 256 bits (32 bytes)
}
$rounds = 10000;
$testVal = "This is some test data";
$encryptionKey = "this is a Secure encryptionKey 12345 ##### 212312"; // encryptionKeys supplied by the user.
$enc = hashRound($rounds, $encryptionKey);
$mbStrlen = mb_strlen($enc);
$encrypted = encrypt256($pass, $testVal);
$decrypted = decrypt256($pass, $encrypted);
echo "<p>\$testVal: $testVal</p>";
echo "<p>\$encryptionKey: $encryptionKey</p>";
echo "<p>\$mbStrlen: $mbStrlen</p>";
echo "<p>\$encrypted: $encrypted</p>";
echo "<p>\$decrypted: $decrypted</p>";
password_hash()
andpassword_verify()
should be strongly considered for this purpose. They represent probably the most authoritative, secure, peer-reviewed, and therefore trusted implementation for this functionality in PHP. \$\endgroup\$