I do not have much to add to Maarten's answer, except I thought it would be nice to show some the code that illustrates his words.
mcrypt
adds zeroes to fill up the plaintext to a multiple of the BF blocksize of 8 bytes, which can be shown by printing hexdumps of both the plaintext and the decrypted ciphertext:
$key = "supersecretkey";
$data = "foobar";
$ctxt = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
$ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
echo bin2hex($data).PHP_EOL;
echo bin2hex($ptxt).PHP_EOL;
gives the following hexdumps:
666f6f626172
666f6f6261720000
openssl
by default uses PKCS#5 padding which, in this case, adds 2 bytes with value 2 at the end of the block:
$key = "supersecretkey";
$data = "foobar";
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
$ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
$ptxt = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $ctxt, MCRYPT_MODE_ECB);
echo bin2hex($data).PHP_EOL;
echo bin2hex($ptxt).PHP_EOL;
gives
666f6f626172
666f6f6261720202
The ciphertext for mcrypt
and openssl
can be made consistent by manually adding the padding bytes. Note the OPENSSL_ZERO_PADDING
options and the addition of "\0\0"
:
$key = "supersecretkey";
$data = "foobar";
$ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data, MCRYPT_MODE_ECB);
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
$ctxt_os = openssl_encrypt($data."\0\0", 'BF-ECB', $key, $opts);
echo bin2hex($ctxt_mc).PHP_EOL;
echo bin2hex($ctxt_os).PHP_EOL;
gives:
e73d2adf1367a24c
e73d2adf1367a24c
Alternatively, manually inserting PKCS#5 padding bytes at the end when using mcrypt
:
$key = "supersecretkey";
$data = "foobar";
$ctxt_mc = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $data."\2\2", MCRYPT_MODE_ECB);
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY;
$ctxt_os = openssl_encrypt($data, 'BF-ECB', $key, $opts);
echo bin2hex($ctxt_mc).PHP_EOL;
echo bin2hex($ctxt_os).PHP_EOL;
gives
d73caa6afabbb029
d73caa6afabbb029
Finally, trying to invoke openssl_encrypt()
with padding disabled and a length that is not a multiple of the block size:
$key = "supersecretkey";
$data = "foobar";
$opts = OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY | OPENSSL_ZERO_PADDING;
$ctxt = openssl_encrypt($data, 'BF-ECB', $key, $opts);
echo(openssl_error_string().PHP_EOL)
gives
error:0607F08A:digital envelope routines:EVP_EncryptFinal_ex:data not multiple of block length
Remark: the name OPENSSL_ZERO_PADDING
is confusing, but it means "no padding". You might be tempted to use the flag OPENSSL_NO_PADDING
, but that one is not intended to be used with openssl_encrypt()
. Its value is 3, which is the same as OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
. In stead, it is intended for use with asymmetric cryptography.