1

From time to time I come around the task of creating functions for encrypting/decrypting strings and files in PHP. I decided to finally nail those functions and did some searching but I couldn't find enough resources to confirm the security of these functions.

Please note that I don't want to use another full-blown library unless necessary and I don't see why PHP provides OpenSSL & mcrypt functions but nobody really implements them.

I was able to find these functions but they are not commented and some steps were unclear (also they do not generate a key but use a predefined one). Following these functions I also found this stackoverflow question but the first answer uses another library while the second one uses ECB.

edit: I updated the code sample which previously utilized mcrypt to using only OpenSSL as suggested in the comments:

function generate_key($cipher = 'AES-256-CBC')
{
    return base64_encode(openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher))); // Generate a random key - currently using the function for the vector length
}
function encrypt($data, $key, $cipher = 'AES-256-CBC')
{
    $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher)); // Generate a random initialization vector
    return base64_encode($iv) . '$' . openssl_encrypt($data, $cipher, base64_decode($key), false, $iv); // Return a base64 encoded string containing the iv and the encrypted data
}
function decrypt($data, $key, $cipher = 'AES-256-CBC')
{
    $data = explode('$', $data); // Explode the previously encoded string

    if(count($data) == 2)
        return openssl_decrypt($data[1], $cipher, base64_decode($key), false, base64_decode($data[0])); // Decrypt the data given key and the iv
    else
        return false;
}

I tested encryption and decryption using these function like this:

$input = 'Hello world!';
echo 'Original data: ' . $input . '<br /><br />';

$key = generate_key();
$encrypted = encrypt($input, $key);
echo 'Key used for encryption: ' . $key . '<br />';
echo 'Encrypted data: ' . $encrypted . '<br /><br />';

$decrypted = decrypt($encrypted, $key);
echo 'Decrypted data: ' . $decrypted . '<br />';

The question: Is OpenSSL properly implemented as shown above? Can they be used for files too?

These are the old functions using mcrypt. Don't use them anymore.

function generate_key($cipher = MCRYPT_RIJNDAEL_256)
{
    return bin2hex(openssl_random_pseudo_bytes(mcrypt_get_key_size($cipher, MCRYPT_MODE_CBC))); // Generate a random key using OpenSSL with size given from mcrypt depending on cipher
}
function encrypt($data, $key, $cipher = MCRYPT_RIJNDAEL_256)
{
    $iv = mcrypt_create_iv(mcrypt_get_iv_size($cipher, MCRYPT_MODE_CBC)); // Generate random initialization vector with size given from mcrypt depending on cipher
    return bin2hex($iv) . '$' . bin2hex(mcrypt_encrypt($cipher, pack('H*', $key), $data, MCRYPT_MODE_CBC, $iv)); // Return the initialization vector and encrypted data as ASCII string
}
function decrypt($data, $key, $cipher = MCRYPT_RIJNDAEL_256)
{
    $data = explode('$', $data); // Split the input data by $ to retrieve the initialization vector and the encrypted data

    if(count($data) == 2) // Check if there are 2 parts after splitting by $
        return mcrypt_decrypt($cipher, pack('H*', $key), pack('H*', $data[1]), MCRYPT_MODE_CBC, pack('H*', $data[0])); // Return the decrypted string
    else
        return false; // Return false if the given data was not properly formatted (no $)
}
Community
  • 1
  • 1
user1137183
  • 119
  • 2
  • 11
  • 1
    It's bit vague what you *question* is. Are you looking for yes/no answer whether this is secure? Are you asking whether the mcrypt and openssl versions are the same? Are you asking how to determine the key size in openssl? Please clarify your question. StackOverflow is not suited for yes/no questions and discussions. Also, please stick to one question per post. – Artjom B. Jun 19 '15 at 13:01
  • 2
    I'm the author of that first answer that uses another library. You might want to also see [this answer](http://stackoverflow.com/a/30189841/2224584) which explains the process for implementing authenticated symmetric-key encryption in PHP. It also demonstrates how to use OpenSSL ([don't use mcrypt](https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-code-you-re-doing-it-wrong)). – Scott Arciszewski Jun 19 '15 at 19:26
  • @ArtjomB. You are right. The original question got a little bit lost. I will update it. The question was if the functions were correct (which they are not) and if/how they can/should be used for files. Your comment and Scott Arciszewskis comment already help alot. I will look into optimizing the second set of functions according to Scotts links. – user1137183 Jun 19 '15 at 20:13
  • 1
    "The question: Is OpenSSL properly implemented as shown above? Can they be used for files too?" [Encryption is not authentication](https://paragonie.com/blog/2015/05/using-encryption-and-authentication-correctly). You aren't authenticating your ciphertexts. Encrypt then MAC, verify MACs before decrypting. For file encryption, [we're working on it](https://github.com/defuse/php-encryption/pull/63). – Scott Arciszewski Jun 21 '15 at 05:26
  • @ScottArciszewski Very good point. Would a SHA-256 hash of the ciphertext suffice for authentication? Maybe appending the hash to the key so it can be stored in the database together? – user1137183 Jun 21 '15 at 15:28
  • 1
    The secure way: Use HKDF to split the key into an encryption key and an authentication key. Use `HMAC-SHA-256` of the ciphertext instead of a simple hash function. Verify the HMAC before decrypting (using `hash_equals()`). – Scott Arciszewski Jun 21 '15 at 16:20
  • @ScottArciszewski Alright, I rewrote the function to work with HMACs. Is there an alternative to hash_equals? It appears it was only added in PHP 5.6 - which is a little bit to recent considering most webservers only run 5.3. – user1137183 Jun 24 '15 at 09:17
  • https://github.com/sarciszewski/php-future – Scott Arciszewski Jun 24 '15 at 10:10

1 Answers1

-1

Current best practise is to avoid using mcrypt in favor of openssl.

And the speed benchmark, where openssl is pretty much faster.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
venca
  • 1,196
  • 5
  • 18
  • Noted. I tried translating the functions above to pure OpenSSL but there appears to be no equivalent for the functions mcrypt_create_iv and mcrypt_get_key_size. How would I create an IV in OpenSSL and get a proper key size for the chosen cipher? PS: The second link is wrong. – user1137183 Jun 19 '15 at 05:02
  • Use openssl_random_pseudo_bytes (most preferably with the second parameter set to TRUE). This will generate IVs with appropriate randomness characteristics. About key size I am not pretty sure, but it won't be hard to google it. – venca Jun 19 '15 at 05:30
  • I updated the question with an OpenSSL version. I couldn't find the "OpenSSL-way" of determining the key size though so I used the vector size for now. Is there a reason why OpenSSL uses 16 Bytes as IV and mcrypt uses 32 Bytes although both use AES-256-CBC? – user1137183 Jun 19 '15 at 07:59
  • 2
    @user The two snippets use different algorithms. Rijndael-256 has a block size of 256bit, but AES (Rijndael-128) has a block size of 128bit. The IV size is the same as the block size. – Artjom B. Jun 19 '15 at 09:01
  • @ArtjomB. So my assumption that mcrypts MCRYPT_RIJNDAEL_256 equals OpenSSLs AES-256-CBC is wrong? What would be the correct constant to achieve the same 256bit algorithm in both snippets? – user1137183 Jun 19 '15 at 09:34
  • 1
    @user MCRYPT_RIJNDAEL_128 – Artjom B. Jun 19 '15 at 09:48
  • I completely disagree. Mcrypt and openssl are not the same thing. Mcrypt is a wrapper that works at a slightly lower level so that's why you need to implement it properly. The algorithms themselves are not changing over time so i don't think it makes sense to say that we should never use mcrypt and always use openssl. – Phil Jun 19 '15 at 20:30
  • @Phil_1984_ While I agree with you that if the library works, it can be used. The problem with crypto libs is that they may contain vulnerabilities. In case of AES it would be entirely possible that some new side channel attack is discovered. This will be a problem when use mcrypt, because it is abandonware. – Artjom B. Jun 19 '15 at 22:01
  • The Rijndael algorithm has been around for 13 years now. If a major flaw is found in the algorithm, any aes implementation will suffer that same flaw. You cannot simply release a patch for rijndael in to aes. More likely what would happen is that over time higher length keys will be used and if a major flaw was found AES would be abandoned for something better. See DES or 3DES – Phil Jun 20 '15 at 10:34
  • Oh i think i see what you mean now. You are talking about the implementation of rijndael in mcrypt. I would say that it is quite unlikely for this to be hacked in some way due to the simplicity of the algorithm, the time that has passed, the fact that it works, and the scrutiny that libraries like these come under. If that was found out I'm sure that the whole of mcrypt will instantly get a bad reputation. – Phil Jun 20 '15 at 10:49