php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73812 openssl_error_string() dubious when decrypting aes-128-ccm
Submitted: 2016-12-25 19:04 UTC Modified: 2017-01-16 13:41 UTC
From: anthon dot pang at gmail dot com Assigned: bukka (profile)
Status: Not a bug Package: OpenSSL related
PHP Version: 7.1.0 OS:
Private report: No CVE-ID: None
 [2016-12-25 19:04 UTC] anthon dot pang at gmail dot com
Description:
------------
openssl_error_string() returns a dubious message, "error:0607A082:digital envelope routines:EVP_CIPHER_CTX_set_key_length:invalid key length" when decrypting even though the payload was successfully decrypted

(In the test script, the payload was produced using sjcl.)


Test script:
---------------
<?php
$password = 'password';
$input    = json_decode('{"iv":"A0DOQPxWAlJ5LHjoyg==","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Kk+ws1Xj0Xo=","ct":"NCPpLCHLLO5mBOGpSUpHdXPgKZA="}', true);
$digest   = hash_pbkdf2('sha256', $password, base64_decode($input['salt']), $input['iter'], 0, true);
$cipher   = $input['cipher'] . '-' . $input['ks'] . '-' . $input['mode'];
$ct       = substr(base64_decode($input['ct']), 0, - $input['ts'] / 8);
$tag      = substr(base64_decode($input['ct']), - $input['ts'] / 8);
$iv       = base64_decode($input['iv']);
$adata    = $input['adata'];

$dt = openssl_decrypt($ct, $cipher, $digest, OPENSSL_RAW_DATA, $iv, $tag, $adata);
var_dump($dt);
while ($msg = openssl_error_string()) {
    echo $msg . "\n";
}

Expected result:
----------------
string(12) "Hello World!"


Actual result:
--------------
string(12) "Hello World!"
error:0607A082:digital envelope routines:EVP_CIPHER_CTX_set_key_length:invalid key length

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-01-09 18:53 UTC] bukka@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: bukka
 [2017-01-09 18:53 UTC] bukka@php.net
Yeah this is expected. It is due to fact that you supplied password that doesn't match the exact block length so it's either filled with 0 bytes (if short) or trimmed (if long). This behaviour cannot be changed due to BC concern.
 [2017-01-09 20:12 UTC] anthon dot pang at gmail dot com
In the context of my example, do you mean $password or $digest needs to match the block length?
 [2017-01-15 21:16 UTC] bukka@php.net
I mean that in your example $password needs to by 16 bytes (128 bits as it's the block size of aes-128)
 [2017-01-15 21:19 UTC] bukka@php.net
Actually missed that you pass digest. In that case it means that $digest needs to be 16 bytes (strlen($digest) === 16)... ;)
 [2017-01-16 03:29 UTC] anthon dot pang at gmail dot com
What are we missing here?

I got the same warning when I changed $password to a 16 character string; and $digest is a hash, so it's always a 32 byte string.

<?php
$password = 'passwordpassword';
$input    = json_decode('{"iv":"A0DOQPxWAlJ5LHjoyg==","v":1,"iter":10000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"iGC2RaDrxUk=","ct":"3X3DKTIIE0VN0SzC9gH8k4wK3DNAsA=="}', true);
$digest   = hash_pbkdf2('sha256', $password, base64_decode($input['salt']), $input['iter'], 0, true);
$cipher   = $input['cipher'] . '-' . $input['ks'] . '-' . $input['mode'];
$ct       = substr(base64_decode($input['ct']), 0, - $input['ts'] / 8);
$tag      = substr(base64_decode($input['ct']), - $input['ts'] / 8);
$iv       = base64_decode($input['iv']);
$adata    = $input['adata'];

$dt = openssl_decrypt($ct, $cipher, $digest, OPENSSL_RAW_DATA, $iv, $tag, $adata);
var_dump($dt);
while ($msg = openssl_error_string()) {
    echo $msg . "\n";
}
 [2017-01-16 13:41 UTC] bukka@php.net
First of all I have to correct myself as it's of course about algorithm key length not block size even it is the same for aes-128 (it means 16 bytes). But for aes-256 the block size is still 128 but key size is 256 (which would be btw fine for your example)

What you are doing wrong is passing 32 bytes digest to openssl_decrypt. It needs to be 16 if you use aes-128 and you don't want to see the error. What happen internally is that it tries to set cipher key length but it won't succeed for AES as its key length cannot be changed (that's why the OpenSSL error). Then the value is trimmed so it means that this 

openssl_decrypt($ct, $cipher, $digest, OPENSSL_RAW_DATA, $iv, $tag, $adata);

is exactly the same as

openssl_decrypt($ct, $cipher, substr($digest, 0, 16), OPENSSL_RAW_DATA, $iv, $tag, $adata);

The second will just not give you an error.

Instead of substr you can specify output size already in hash_pbkdf2. In your case:

hash_pbkdf2('sha256', $password, base64_decode($input['salt']), $input['iter'], 16, true)
 
PHP Copyright © 2001-2022 The PHP Group
All rights reserved.
Last updated: Mon Dec 05 19:03:46 2022 UTC