Search code examples
phpopensslmcrypt

Why are mcrypt and openssl_encrypt not giving the same results for blowfish with ecb if password is shorter than 16 chars?


I've read the general suggesstions: mcrypt is deprecated, what is the alternative? and PHP7.1 mcrypt alternative

And tested the following solutions: Migrating mcrypt with Blowfish and ECB to OpenSSL and php: mcrypt_encrypt to openssl_encrypt, and OPENSSL_ZERO_PADDING problems

They ain't work. This is my used code:

$message = "My secret message";
$key = "mysecretpasswor"; // <- if you add one more character here, it's working
$iv = "\0\0\0\0\0\0\0\0";

$message_padded = $message;

if(strlen($message_padded) % 8) {
  $message_padded = str_pad($message_padded, strlen($message_padded) + 8 - strlen($message_padded) % 8, "\0");
}

$encrypted_mcrypt = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $message, MCRYPT_MODE_ECB, $iv);
$encrypted_openssl = openssl_encrypt($message_padded, "bf-ecb", $key, OPENSSL_RAW_DATA | OPENSSL_NO_PADDING);

printf("%s => %s\n", $message, base64_encode($encrypted_mcrypt));
printf("%s => %s\n", $message_padded, base64_encode($encrypted_openssl));

Using something like "DES-EDE3-CBC" as encryption method does work. But I cannot change the encryption that's used. I've to migrate the old code to new code. There sometimes keys used, that are shorter than 16 characters.

Any suggestions?


Solution

  • First of all, OPENSSL_NO_PADDING is not supposed to be used with openssl_encrypt(). Its documentation mentions OPENSSL_ZERO_PADDING, which (confusingly) means 'no padding'. That is what you want.

    OPENSSL_NO_PADDING is intended for use with asymmetric cryptography. By coincidence it has a value of 3 which is equal to OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING. This the reason why you can use it incorrectly without consequence (in this case).

    Your ciphertexts are different because the function openssl_encrypt() in bf-ecb mode by default will pad your key with \0's if its length is less than 16 bytes. This is not required for blowfish and mcrypt_encrypt() does not do that. In order to switch off that behavior, use the flag OPENSSL_DONT_ZERO_PAD_KEY when calling openssl_encrypt(). Since this flag does not seem to be documented, you will have to go to the source code to learn about it :-). Or read Bug #72362 OpenSSL Blowfish encryption is incorrect for short keys.

    With this, the correct invocation of openssl_encrypt() becomes:

    $opts = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING | OPENSSL_DONT_ZERO_PAD_KEY;
    $encrypted_openssl = openssl_encrypt($message_padded, "bf-ecb", $key, $opts);
    

    Testing it:

    $ php bf.php
    My secret message => JPO/tvAqFD2KCTqfv0l8uWLfPUWdZIxQ
    My secret message => JPO/tvAqFD2KCTqfv0l8uWLfPUWdZIxQ