magento2: M2.3 – Sodium crypto adapter errors on unexpected input

Under certain conditions – in my case resulting from a 2.2.6 -> 2.3.0 upgrade – the cryptography adapter \Magento\Framework\Encryption\Adapter\SodiumChachaIetf::decrypt does not return a string, despite the strict return type on its signature. This results in exceptions and failure to bootstrap Magento.

Preconditions (*)

  1. Upgraded Magento 2.2.6 -> 2.3 instance.
  2. Unknown conditions – possibly non-usable crypt key or corrupt encrypted system config values.

Steps to reproduce (*)

  1. Attempt to run bin/magento
  2. Observe error output similar to:
Type Error occurred when creating object: Magento\Framework\Interception\Config\Config

Expected result (*)

  1. The CLI tool command list should be presented.

Actual result (*)

  1. An error is presented.

Research

I acknowledge that this may be difficult to reproduce, given that I have yet to understand the precise conditions. What I can tell you is that this originated in a Commerce Cloud staging environment (but affects all 2.3 versions), but did not affect Cloud integration environments. So it may have been tied to my configuration differences between environments.

However, upon investigation I found that the given “Type Error” had an underlying detail in system.log:

[2018-12-05 14:29:34] report.CRITICAL: Type Error occurred when creating object: Magento\Framework\Interception\Config\Config, Return value of Magento\Framework\Encryption\Adapter\SodiumChachaIetf::decrypt() must be of the type string, boolean returned [] []

This led me to study the new cryptography library, and what I discovered was:

  • SodiumChachaIetf::decrypt declares a strict string return type.
  • The underlying polyfill sodium_crypto_aead_chacha20poly1305_ietf_decrypt annotates @return string but actually can return a boolean.
    • The actual PHP function has no documentation to describe the expected return type.
  • An exception was actually thrown under certain conditions.

Given that the return type is strictly string on the decrypt method, and that no @throws annotations are presented, this should require that its implementation does whatever is necessary to ensure only a string is returned.

Based on that assumption, I’ve created a PR (#19591 ), which does just that. Also assumed, was that in the event of decryption failure, the original input string is returned. This might not be the best assumption but it works for my use.

Note on Encryptor

This is tangentially related, but I also discovered that if you pass a malformed $hash to \Magento\Framework\Encryption\Encryptor::isValidHash, it will forward the input to explodePasswordHash, which causes an input type error on explode. You can observe this with the following sample code:

$a = \Magento\Framework\App\ObjectManager::getInstance()->create(
    '\Magento\Framework\Encryption\Encryptor'
);
$b = \Magento\Framework\App\ObjectManager::getInstance()->create(
    '\Magento\Framework\Encryption\Encryptor'
);

var_dump($a->isValidHash('test-input', 'correct:input:format'));
var_dump($b->isValidHash('test-input', 'incorrect-input-format'));

According to the explodePasswordHash signature you should not be throwing an exception. So a more graceful handling of bad input ought to be considered.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 9
  • Comments: 22 (5 by maintainers)

Most upvoted comments

tl;dr - check your crypt key matches where the database came from

I had a very similar issue on Magento 2.3, both on php7.1 and php7.2. Many of the same error messages reported here such as SodiumChachaIetf::decrypt() must be of the type string, boolean returned and Type Error occurred when creating object: Magento\Config\Model\Config\Structure\Interceptor

However, I got a stack trace that showed part of the config string it was attempting to decrypt, and this led me to double check my crypt key… turns out it didn’t match between my local environment and the production machine. Iirc it used to fail “silently” if the crypt keys didn’t match, where it would simply return an empty string if you tried to access an encrypted config.

As @beargroup-jay mentioned, If you are upgrading from old DB, in app/etc/env.php file simply add the crypt key from your previous configurations. For me I updated and it works fine.

This work for me Modify this file : vendor/magento/framework/Encryption/Adapter/SodiumChachaIetf.php

$plainText = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
            $payload,
            $nonce,
            $nonce,
            $this->key
        );
        if ($plainText == false)
        {
          return "";
        }
        return $plainText;

Hello @vbuck @andykim @evagabond @tig-daanvandenbergh @syno-bvo @

Thank you for contribution and collaboration!

The corresponding internal ticket MAGETWO-96983 was fixed, delivered and closed by Magento team

Please see details in the next commit:

  • 2a65cc6

I found the way to resole it in my case. In table core_config_data , find these paths and remove their value (It happens because the paypal encrypt in magento 1 cannot decrypt on magento 2).

paypal/wpp/api_username paypal/wpp/api_password

Hope can help you!

I just wanted to let you guys know, I ran into this issue while running integration tests for M2.3.1. @abdulkaderptp’s comment inspired me to find out the mocked database configuration. This can be found in /dev/tests/integration/etc/install-config-mysql.php. The fix is to include the crypt-key, like this:

return [
    'db-host'           => 'MAGENTO_DB_HOST',
    'db-user'           => 'MAGENTO_DB_USER',
    'db-password'       => 'MAGENTO_DB_PASS',
    'db-name'           => 'MAGENTO_DB_NAME',
    'db-prefix'         => '',
    'key'               => 'myrandomcryptkey',
    'backend-frontname' => 'admin',
    'admin-user'        => \Magento\TestFramework\Bootstrap::ADMIN_NAME,
    'admin-password'    => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD,
    'admin-email'       => \Magento\TestFramework\Bootstrap::ADMIN_EMAIL,
    'admin-firstname'   => \Magento\TestFramework\Bootstrap::ADMIN_FIRSTNAME,
    'admin-lastname'    => \Magento\TestFramework\Bootstrap::ADMIN_LASTNAME,
];

tl;dr - check your crypt key matches where the database came from

I had a very similar issue on Magento 2.3, both on php7.1 and php7.2. Many of the same error messages reported here such as SodiumChachaIetf::decrypt() must be of the type string, boolean returned and Type Error occurred when creating object: Magento\Config\Model\Config\Structure\Interceptor

However, I got a stack trace that showed part of the config string it was attempting to decrypt, and this led me to double check my crypt key… turns out it didn’t match between my local environment and the production machine. Iirc it used to fail “silently” if the crypt keys didn’t match, where it would simply return an empty string if you tried to access an encrypted config. Yes, This is the cause, for me the I copied env.php file from other project to new project, the file include key from other project: ‘crypt’ => [ ‘key’ => ‘14adcaa76daf130fdfdfdfdefdf’ ], this is the cause, I have to get key from current project then replace it then it works

tl;dr - check your crypt key matches where the database came from I had a very similar issue on Magento 2.3, both on php7.1 and php7.2. Many of the same error messages reported here such as SodiumChachaIetf::decrypt() must be of the type string, boolean returned and Type Error occurred when creating object: Magento\Config\Model\Config\Structure\Interceptor However, I got a stack trace that showed part of the config string it was attempting to decrypt, and this led me to double check my crypt key… turns out it didn’t match between my local environment and the production machine. Iirc it used to fail “silently” if the crypt keys didn’t match, where it would simply return an empty string if you tried to access an encrypted config. Yes, This is the cause, for me the I copied env.php file from other project to new project, the file include key from other project: ‘crypt’ => [ ‘key’ => ‘14adcaa76daf130fdfdfdfdefdf’ ], this is the cause, I have to get key from current project then replace it then it works

Yes for me too it was due to similar issue viz. key mismatch.

I was trying to create a local copy of my remote site. I copied all the files from the remote site, but tried to use the old ‘env.php’ file that existed in the local site. Once I used the ‘env.php’ file of the remote site, this error has gone!

Yes, it is quite correct. If you have got a database from production environment you need to take a production encryption key as well. There are some values in the DB which are sensitive to encryption key. For example, PayPal credentials are encrypted this way.

Below solution work for me : Go to this file:

vendor/magento/framework/Encryption/Adapter/SodiumChachaIetf.php

And update below code:

 public function decrypt(string $data): string
    {
        $nonce = mb_substr($data, 0, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, '8bit');
        $payload = mb_substr($data, SODIUM_CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, null, '8bit');

        $plainText = sodium_crypto_aead_chacha20poly1305_ietf_decrypt(
            $payload,
            $nonce,
            $nonce,
            $this->key
        );

        return (string) $plainText;
    }

Just change the function return type: From

return $plainText

to

return (string) $plainText

We ran into this exact issue on a fresh 2.3.0 install w/ php 7.2 w/ Magento Sample data.

Resolution:

composer update upgrade re-compile.

Solved.

Maybe it will help others.

Does anyone have a quick fix for it? @hostep