anjanesh
anjanesh

Reputation: 4251

Run openssl encryption functions faster

This is a question related to security posted here.

I am using this in my current PHP 7.0 setup which works fine. But because mcrypt has been replaced with openssl since 7.2, I am working to update the encrypt and decrypt functions with the ones posted here since it's built-in.

But because this is on a webpage with 25 items, it's taking a lot of time to execute which is unacceptable to the end-user.

<?php
define("ENCRYPTION_KEY", "!@#$%^&*");

$StartTime = microtime(TRUE);

$e = [];
for ($i = 0; $i < 25; $i++)
{
    $e[] = encrypt($i, ENCRYPTION_KEY);
}

echo number_format(microtime(TRUE) - $StartTime, 3)." seconds\n";

# https://stackoverflow.com/a/50373095/126833
function sign($message, $key) {
    return hash_hmac('sha256', $message, $key) . $message;
}

function verify($bundle, $key) {
    return hash_equals(
      hash_hmac('sha256', mb_substr($bundle, 64, null, '8bit'), $key),
      mb_substr($bundle, 0, 64, '8bit')
    );
}

function getKey($password, $keysize = 16) {
    return hash_pbkdf2('sha256',$password,'some_token',100000,$keysize,true);
}

function encrypt($message, $password) {
    $iv = random_bytes(16);
    $key = getKey($password);
    $result = sign(openssl_encrypt($message,'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv), $key);
    return bin2hex($iv).bin2hex($result);
}

function decrypt($hash, $password) {
    $iv = hex2bin(substr($hash, 0, 32));
    $data = hex2bin(substr($hash, 32));
    $key = getKey($password);
    if (!verify($data, $key)) {
      return null;
    }
    return openssl_decrypt(mb_substr($data, 64, null, '8bit'),'aes-256-ctr',$key,OPENSSL_RAW_DATA,$iv);
}
?>

.

$ php encrypt-decrypt.php 
6.288 seconds

Is there any way to execute this real fast ? (Like less than a second for 25 iterations)

Upvotes: 1

Views: 1485

Answers (1)

Maarten Bodewes
Maarten Bodewes

Reputation: 93978

The number of iterations of PBKDF2 is called the work factor. If the number of iterations is high then the work factor, the amount of work that PBKDF2 needs to perform, is high as well. Note that the number of iterations is linear with the amount of work to be performed, and computers get faster (close to) exponentially.

Note that PBKDF2 implementations can be sped up both in hardware and software. So beware that an attacker will be able to execute them faster. 100,000 is actually a recommended amount.


Your implementation correctly calls PBKDF2 for a small amount of bytes (smaller than the output of the hash value. If you do not do this then PBKDF2 is basically called twice, while the attacker may get away with calling it just once to verify a password guess. So beware that you don't change the code to ask for two keys, which would only slow your server down.

However, you call $key = getKey($password); within the encrypt function. This means that the key is derived for each call. This is of course not very smart: you should cache the key until it is not needed anymore, and destroy it directly thereafter. There is no need to derive the key 25 times. Just lowering the iteration is a makeshift solution that hands back a lot of advantage to an attacker.

Upvotes: 3

Related Questions