CVE-2020-11031 (PHP, published 2020-09-23T16:15Z)
Description: value: In GLPI before version 9.5.0, the encryption algorithm used is insecure. The security of the data encrypted relies on the password used, if a user sets a weak/predictable password, an attacker could decrypt data. This is fixed in version 9.5.0 by using a more secure encryption library. The library chosen is sodium.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-327 \u2014 Use of a Broken or Risky Cryptographic Algorithm
The product uses a broken or risky cryptographic algorithm or protocol.
Repository: https://github.com/glpi-project/glpi
Files changed:
.gitignore
CHANGELOG.md
ajax/mailcollector.php
composer.json
composer.lock
inc/auth.class.php
inc/authldap.class.php
inc/config.class.php
inc/console/database/installcommand.class.php
inc/console/security/changekeycommand.class.php
inc/dashboard/grid.class.php
inc/glpikey.class.php
inc/glpimailer.class.php
inc/mailcollector.class.php
inc/marketplace/api/plugins.class.php
inc/rssfeed.class.php
inc/system/requirementsmanager.class.php
inc/toolbox.class.php
inc/update.class.php
install/install.php
install/update_0782_080.php
install/update_94_95.php
status.php
tests/functionnal/AuthLdap.php
tests/imap/MailCollector.php
tests/units/Toolbox.php
Commit message: Merge pull request from GHSA-7xwm-4vjr-jvqh

* Deprecate GLPIKEY usage

CVE-2020-5248

Deprecate GLPIKEY usage, and replace it with key file per instance.
Add a command to generate new key, and update database.
Add plugins hooks to rgister fields or configuration entries to be
handled when updating db.

* Rely on sodium compat for encryption/decryption

New name for key file, handle migration
Add not required sodium extension
Deprecate, fill changelog, drop old keyfile
Key must be generated from dedicated method

Vulnerable code:
               $input["passwd"] = Toolbox::encrypt(stripslashes($input["passwd"]), GLPIKEY);
        "ext-ldap": "Used to provide LDAP authentication and synchronization"
    "content-hash": "267af5215d3b1148d5a2bd588feec8fa",
    }
                                                  Toolbox::decrypt($ldap_method["rootdn_passwd"],
                                                                   GLPIKEY),
}
            $input["rootdn_passwd"] = Toolbox::encrypt(stripslashes($input["rootdn_passwd"]),
                                                       GLPIKEY);
                                  Toolbox::decrypt($config_ldap->fields['rootdn_passwd'], GLPIKEY),
                                    Toolbox::decrypt($this->fields['rootdn_passwd'], GLPIKEY),
                                  Toolbox::decrypt($ldap_method['rootdn_passwd'], GLPIKEY),
                                        Toolbox::decrypt($ldap_method['rootdn_passwd'], GLPIKEY),
                                Toolbox::decrypt($authldap->getField('rootdn_passwd'), GLPIKEY),
         $input["rootdn_passwd"] = Toolbox::encrypt(stripslashes($input["rootdn_passwd"]), GLPIKEY);
            $input["smtp_passwd"] = Toolbox::encrypt(stripslashes($input["smtp_passwd"]), GLPIKEY);
            $input["proxy_passwd"] = Toolbox::encrypt(stripslashes($input["proxy_passwd"]),
                                                      GLPIKEY);
      $seed         = $dasboard.$entities_id.$is_recursive.GLPIKEY;
}
            $this->Password = Toolbox::decrypt($CFG_GLPI['smtp_passwd'], GLPIKEY);
            $input["passwd"] = Toolbox::encrypt(stripslashes($input["passwd"]), GLPIKEY);
         'password'  => Toolbox::decrypt($this->fields['passwd'], GLPIKEY),
            Toolbox::decrypt($this->fields['passwd'], GLPIKEY),
               Toolbox::decrypt($this->fields['passwd'], GLPIKEY),
            ? $CFG_GLPI["proxy_user"].":".Toolbox::decrypt($CFG_GLPI["proxy_passwd"], GLPIKEY)."@"
                                             Toolbox::decrypt($CFG_GLPI["proxy_passwd"],
                                                              GLPIKEY);
   static function encrypt($string, $key) {
   static function decrypt($string, $key) {
               CURLOPT_PROXYUSERPWD => $CFG_GLPI["proxy_user"] . ":" . self::decrypt($CFG_GLPI["proxy_passwd"], GLPIKEY),
         die('Upgrade from command line is not supported before 0.72.3!');
                         SET `proxy_passwd` = '".addslashes(Toolbox::encrypt($value, GLPIKEY))."'
                         SET `smtp_passwd` = '".addslashes(Toolbox::encrypt($value, GLPIKEY))."'
                            SET `rootdn_passwd` = '".addslashes(Toolbox::encrypt($data['rootdn_password'],
                                                                                 GLPIKEY))."'
                            SET `passwd` = '".addslashes(Toolbox::encrypt($data['password'],
                                                                          GLPIKEY))."'
                                            Toolbox::decrypt($method["rootdn_passwd"],
                                            GLPIKEY))) {
      //rootdn_passwd is set with a value (a password, not encrypted)
      $password = 'toto';
      $input    = ['name' => 'ldap', 'rootdn_passwd' => $password];
      $result   = $ldap->prepareInputForUpdate($input);

      //Expected value to be encrypted using GLPIKEY key
      $expected = \Toolbox::encrypt(stripslashes($password), GLPIKEY);
      $this->string($result['rootdn_passwd'])->isIdenticalTo($expected);

      $password = 'tot\'o';
      $input    = ['name' => 'ldap', 'rootdn_passwd' => $password];
      $result   = $ldap->prepareInputForUpdate($input);

      //Expected value to be encrypted using GLPIKEY key
      $expected = \Toolbox::encrypt(stripslashes($password), GLPIKEY);
      $this->string($result['rootdn_passwd'])->isIdenticalTo($expected);

      $input['_blank_passwd'] = 1;
      $result   = $ldap->prepareInputForUpdate($input);
      //rootdn_passwd is set but empty
      $this->string($result['rootdn_passwd'])->isEmpty();

            'passwd'    => \Toolbox::encrypt($oinput["passwd"], GLPIKEY),
}
   /**
    * @dataProvider encryptProvider
    */
   public function testEncrypt($string, $key, $expected) {
      $this->string(\Toolbox::encrypt($string, $key))->isIdenticalTo($expected);
    * @dataProvider encryptProvider
   public function testDecrypt($expected, $key, $string) {
      $this->string(\Toolbox::decrypt($string, $key))->isIdenticalTo($expected);

Fixed code:
/config/glpi.key
/config/glpicrypt.key
- Encryption alogithm improved using libsodium
- Usage of `GLPIKEY` constant
- `Toolbox::encrypt()` and `Toolbox::decrypt()` because they use the old encryption aglogithm
               $input["passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["passwd"]));
        "paragonie/sodium_compat": "^1.13",
        "paragonie/random_compat": "*",
        "ext-ldap": "Used to provide LDAP authentication and synchronization",
        "ext-sodium": "Used to provide strong encryption for sensitive data in database"
    "content-hash": "889748826910604c38cf291aab5b831e",
            "funding": [
                {
                    "url": "https://github.com/blueimp",
                    "type": "github"
                }
            ],
            "funding": [
                {
                    "url": "https://funding.communitybridge.org/projects/laminas-project",
                    "type": "community_bridge"
                }
            ],
            "funding": [
                {
                    "url": "https://funding.communitybridge.org/projects/laminas-project",
                    "type": "community_bridge"
                }
            ],
        {
            "name": "paragonie/sodium_compat",
            "version": "v1.13.0",
            "source": {
                "type": "git",
                "url": "https://github.com/paragonie/sodium_compat.git",
                "reference": "bbade402cbe84c69b718120911506a3aa2bae653"
            },
            "dist": {
                "type": "zip",
                "url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/bbade402cbe84c69b718120911506a3aa2bae653",
                "reference": "bbade402cbe84c69b718120911506a3aa2bae653",
                "shasum": ""
            },
            "require": {
                "paragonie/random_compat": ">=1",
                "php": "^5.2.4|^5.3|^5.4|^5.5|^5.6|^7|^8"
            },
            "require-dev": {
                "phpunit/phpunit": "^3|^4|^5|^6|^7"
            },
            "suggest": {
                "ext-libsodium": "PHP < 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security.",
                "ext-sodium": "PHP >= 7.0: Better performance, password hashing (Argon2i), secure memory management (memzero), and better security."
            },
            "type": "library",
            "autoload": {
                "files": [
                    "autoload.php"
                ]
            },
            "notification-url": "https://packagist.org/downloads/",
            "license": [
                "ISC"
            ],
            "authors": [
                {
                    "name": "Paragon Initiative Enterprises",
                    "email": "security@paragonie.com"
                },
                {
                    "name": "Frank Denis",
                    "email": "jedisct1@pureftpd.org"
                }
            ],
            "description": "Pure PHP implementation of libsodium; uses the PHP extension if it exists",
            "keywords": [
                "Authentication",
                "BLAKE2b",
                "ChaCha20",
                "ChaCha20-Poly1305",
                "Chapoly",
                "Curve25519",
                "Ed25519",
                "EdDSA",
                "Edwards-curve Digital Signature Algorithm",
                "Elliptic Curve Diffie-Hellman",
                "Poly1305",
                "Pure-PHP cryptography",
                "RFC 7748",
                "RFC 8032",
                "Salpoly",
                "Salsa20",
                "X25519",
                "XChaCha20-Poly1305",
                "XSalsa20-Poly1305",
                "Xchacha20",
                "Xsalsa20",
                "aead",
                "cryptography",
                "ecdh",
                "elliptic curve",
                "elliptic curve cryptography",
                "encryption",
                "libsodium",
                "php",
                "public-key cryptography",
                "secret-key cryptography",
                "side-channel resistant"
            ],
            "time": "2020-03-20T21:48:09+00:00"
        },
            "funding": [
                {
                    "url": "https://marcus.bointon.com/donations/",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/Synchro",
                    "type": "github"
                },
                {
                    "url": "https://www.patreon.com/marcusbointon",
                    "type": "patreon"
                }
            ],
            "funding": [
                {
                    "url": "https://github.com/ramsey",
                    "type": "github"
                }
            ],
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
            "funding": [
                {
                    "url": "https://symfony.com/sponsor",
                    "type": "custom"
                },
                {
                    "url": "https://github.com/fabpot",
                    "type": "github"
                },
                {
                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
                    "type": "tidelift"
                }
            ],
    },
    "plugin-api-version": "1.1.0"
                                                  Toolbox::sodiumDecrypt($ldap_method["rootdn_passwd"]),
}
            $input["rootdn_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["rootdn_passwd"]));
                                  Toolbox::sodiumDecrypt($config_ldap->fields['rootdn_passwd']),
                                    Toolbox::sodiumDecrypt($this->fields['rootdn_passwd']),
                                  Toolbox::sodiumDecrypt($ldap_method['rootdn_passwd']),
                                        Toolbox::sodiumDecrypt($ldap_method['rootdn_passwd']),
                                Toolbox::sodiumDecrypt($authldap->getField('rootdn_passwd')),
         $input["rootdn_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["rootdn_passwd"]));
            $input["smtp_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["smtp_passwd"]));
            $input["proxy_passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["proxy_passwd"]));
               [ 'name'    => 'paragonie/sodium_compat',
                 'check'   => 'ParagonIE_Sodium_Compat' ],
            ],
            'sodium' => [
               'required' => false
use GLPIKey;
   /**
    * Error code returned when failing to create encryption key file.
    *
    * @var integer
    */
   const ERROR_CANNOT_CREATE_ENCRYPTION_KEY_FILE = 9;

      // Create security key
      $glpikey = new GLPIKey();
      if (!$glpikey->keyExists() && !$glpikey->generate()) {
         $message = __('Security key cannot be generated!');
         $output->writeln('<error>' . $message . '</error>', OutputInterface::VERBOSITY_QUIET);
         return self::ERROR_CANNOT_CREATE_ENCRYPTION_KEY_FILE;
      }

<?php
/**
 * ---------------------------------------------------------------------
 * GLPI - Gestionnaire Libre de Parc Informatique
 * Copyright (C) 2015-2018 Teclib' and contributors.
 *
 * http://glpi-project.org
 *
 * based on GLPI - Gestionnaire Libre de Parc Informatique
 * Copyright (C) 2003-2014 by the INDEPNET Development Team.
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * GLPI is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * GLPI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GLPI. If not, see <http://www.gnu.org/licenses/>.
 * ---------------------------------------------------------------------
 */

namespace Glpi\Console\Security;

if (!defined('GLPI_ROOT')) {
   die("Sorry. You can't access this file directly");
}

use Glpi\Console\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use GLPIKey;

class ChangekeyCommand extends AbstractCommand {
   /**
    * Error code returned when unable to renew key.
    *
    * @var integer
    */
   const ERROR_UNABLE_TO_RENEW_KEY = 1;

   protected function configure() {
      parent::configure();

      $this->setName('glpi:security:change_key');
      $this->setDescription(__('Change password storage key and update values in database.'));
   }

   protected function execute(InputInterface $input, OutputInterface $output) {
      $glpikey = new GLPIKey();

      $fields = $glpikey->getFields();
      $configs = $glpikey->getConfigs();
      $conf_count = 0;
      foreach ($configs as $config) {
         $conf_count += count($config);
      }

      $output->writeln(
         sprintf(
            '<info>' . __('Found %1$s field(s) and %2$s configuration entries requiring migration.') . '</info>',
            count($fields),
            $conf_count
         )
      );

      if (!$input->getOption('no-interaction')) {
         // Ask for confirmation (unless --no-interaction)
         $question_helper = $this->getHelper('question');
         $run = $question_helper->ask(
            $input,
            $output,
            new ConfirmationQuestion(__('Do you want to continue ?') . ' [Yes/no]', true)
         );
         if (!$run) {
            $output->writeln(
               '<comment>' . __('Aborted.') . '</comment>',
               OutputInterface::VERBOSITY_VERBOSE
            );
            return 0;
         }
      }

      $created = $glpikey->generate();
      if (!$created) {
         $output->writeln(
            '<error>' . __('Unable to change security key!') . '</error>',
            OutputInterface::VERBOSITY_QUIET
         );
         return self::ERROR_UNABLE_TO_RENEW_KEY;
      }

      $this->output->write(PHP_EOL);

      $output->writeln('<info>' . __('New security key generated; database updated.') . '</info>');

      return 0; // Success
   }
}
      $seed         = $dasboard.$entities_id.$is_recursive.Telemetry::getInstanceUuid();
}
<?php
/**
 * ---------------------------------------------------------------------
 * GLPI - Gestionnaire Libre de Parc Informatique
 * Copyright (C) 2015-2018 Teclib' and contributors.
 *
 * http://glpi-project.org
 *
 * based on GLPI - Gestionnaire Libre de Parc Informatique
 * Copyright (C) 2003-2014 by the INDEPNET Development Team.
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * GLPI is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * GLPI is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GLPI. If not, see <http://www.gnu.org/licenses/>.
 * ---------------------------------------------------------------------
 */

if (!defined('GLPI_ROOT')) {
   die("Sorry. You can't access this file directly");
}

/**
 *  GLPI security key
**/
class GLPIKey {
   /**
    * Key file path.
    *
    * @var string
    */
   private $keyfile;

   /**
    * Legacy key file path.
    *
    * @var string
    */
   private $legacykeyfile;

   /**
    * List of crypted DB fields.
    *
    * @var array
    */
   protected $fields = [
      'glpi_mailcollectors.passwd',
      'glpi_authldaps.rootdn_passwd'
   ];

   /**
    * List of crypted configuration values.
    * Each key corresponds to a configuration context, and contains list of configs names.
    *
    * @var array
    */
   protected $configs = [
      'core'   => [
         'smtp_passwd',
         'proxy_passwd'
      ]
   ];

   public function __construct() {
      $this->keyfile = GLPI_CONFIG_DIR . '/glpicrypt.key';
      $this->legacykeyfile = GLPI_CONFIG_DIR . '/glpi.key';
   }

   /**
    * Check if GLPI security key used for decryptable passwords exists
    *
    * @return string
    */
   public function keyExists() {
      return file_exists($this->keyfile) && !empty($this->get());
   }

   /**
    * Get GLPI security key used for decryptable passwords
    *
    * @throw \RuntimeException if key file is missing
    *
    * @return string
    */
   public function get() {
      if (!file_exists($this->keyfile)) {
         throw new \RuntimeException('You must create a security key, see glpi:security:change_key command.');
      }
      //load key from existing config file
      $key = file_get_contents($this->keyfile);
      return $key;
   }

   /**
    * Get GLPI security legacy key that was used for decryptable passwords
    *
    * @return string
    *
    * @deprecated 9.5.0
    */
   public function getLegacyKey() {
      Toolbox::deprecated();

      if (!file_exists($this->legacykeyfile)) {
         return GLPIKEY;
      }
      //load key from existing config file
      $key = file_get_contents($this->legacykeyfile);
      return $key;
   }

   /**
    * Generate GLPI security key used for decryptable passwords
    * and update values in DB if necessary.
    * @return boolean
    */
   public function generate() {
      global $DB;

      $sodium_key = null;
      $old_key = false;

      try {
         $sodium_key = $this->get();
      } catch (\RuntimeException $e) {
         $sodium_key = null;
         $old_key = @$this->getLegacyKey();
      }

      $key = sodium_crypto_aead_chacha20poly1305_ietf_keygen();
      $success = (bool)file_put_contents($this->keyfile, $key);
      if (!$success) {
         return false;
      }

      if ($DB instanceof DBmysql) {
         return $this->migrateFieldsInDb($sodium_key, $old_key)
            && $this->migrateConfigsInDb($sodium_key, $old_key);
      }

      return true;
   }

   /**
    * Get fields
    *
    * @return array
    */
   public function getFields() :array {
      global $PLUGIN_HOOKS;

      $fields = $this->fields;
      if (isset($PLUGIN_HOOKS['secured_fields'])) {
         foreach ($PLUGIN_HOOKS['secured_fields'] as $plugfields) {
            $fields = array_merge($fields, $plugfields);
         }
      }

      return $fields;
   }

   /**
    * Get configs
    *
    * @return array
    */
   public function getConfigs() :array {
      global $PLUGIN_HOOKS;

      $configs = $this->configs;

      if (isset($PLUGIN_HOOKS['secured_configs'])) {
         foreach ($PLUGIN_HOOKS['secured_configs'] as $plugin => $plugconfigs) {
            $configs['plugin:' . $plugin] = $plugconfigs;
         }
      }

      return $configs;
   }

   /**
    * Migrate fields in database
    *
    * @param string       $sodium_key Current key
    * @param string|false $old_key     Old key, if any
    *
    * @return void
    */
   protected function migrateFieldsInDb($sodium_key, $old_key = false) {
      global $DB;

      $success = true;

      foreach ($this->getFields() as $field) {
         list($table, $column) = explode('.', $field);

         $iterator = $DB->request([
            'SELECT' => ['id', $column],
            'FROM'   => $table
         ]);

         while ($success && $row = $iterator->next()) {
            if ($old_key === false) {
               $pass = Toolbox::sodiumEncrypt(Toolbox::sodiumDecrypt($row[$column], $sodium_key));
            } else {
               $pass = Toolbox::sodiumEncrypt(Toolbox::decrypt($row[$column], $old_key));
            }
            $success = $DB->update(
               $table,
               [$field  => $pass],
               ['id'    => $row['id']]
            );
         }
      }

      return $success;
   }

   /**
    * Migrate configurations in database
    *
    * @param string       $sodium_key Current key
    * @param string|false $old_key    Old key, if any
    *
    * @return boolean
    */
   protected function migrateConfigsInDb($sodium_key, $old_key = false) {
      global $DB;

      $success = true;

      foreach ($this->getConfigs() as $context => $names) {
         $iterator = $DB->request([
            'FROM'   => Config::getTable(),
            'WHERE'  => [
               'context'   => $context,
               'name'      => $names
            ]
         ]);

         while ($success && $row = $iterator->next()) {
            if ($old_key === false) {
               $pass = Toolbox::sodiumEncrypt(Toolbox::sodiumDecrypt($row['value'], $sodium_key));
            } else {
               $pass = Toolbox::sodiumEncrypt(Toolbox::decrypt($row['value'], $old_key));
            }
            $success = $DB->update(
               Config::getTable(),
               ['value' => $pass],
               ['id'    => $row['id']]
            );
         }
      }

      return $success;
   }
}
            $this->Password = Toolbox::sodiumDecrypt($CFG_GLPI['smtp_passwd']);
            $input["passwd"] = Toolbox::sodiumEncrypt(stripslashes($input["passwd"]));
         'password'  => Toolbox::sodiumDecrypt($this->fields['passwd']),
            Toolbox::sodiumDecrypt($this->fields['passwd']),
               Toolbox::sodiumDecrypt($this->fields['passwd']),
            ? $CFG_GLPI["proxy_user"].":".Toolbox::sodiumDecrypt($CFG_GLPI["proxy_passwd"])."@"
                                             Toolbox::sodiumDecrypt($CFG_GLPI["proxy_passwd"]);
      $requirements[] = new Extension('sodium', true);
   static function encrypt($string, $key = null) {
      self::deprecated('Use sodiumEncrypt');

      if ($key === null) {
         $glpikey = new GLPIKey();
         $key = $glpikey->getLegacyKey();
      }
   public static function sodiumEncrypt($content, $key = null) {
      if ($key === null) {
         $key = self::getGlpiSecKey();
      }

      $nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES); // NONCE = Number to be used ONCE, for each message
      $encrypted = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
         $content,
         $nonce,
         $nonce,
         $key
      );
      return base64_encode($nonce . $encrypted);
   }

   public static function sodiumDecrypt($content, $key = null) {
      if ($key === null) {
         $key = self::getGlpiSecKey();
      }

      $content = base64_decode($content);
      $nonce = mb_substr($content, 0, SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES, '8bit');
      $ciphertext = mb_substr($content, SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES, null, '8bit');
      $plaintext = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(
         $ciphertext,
         $nonce,
         $nonce,
         $key
      );
      if (!is_string($plaintext)) {
         throw new \RuntimeException('Unable to decrypt content');
      }
      return $plaintext;
   }
   static function decrypt($string, $key = null) {
      self::deprecated('Use sodiumDecrypt');

      if ($key === null) {
         $glpikey = new GLPIKey();
         $key = $glpikey->getLegacyKey();
      }
   /**
    * Get GLPI security key used for decryptable passwords from file
    *
    * @throw \RuntimeException if key file is missing
    *
    * @return string
    */
   public static function getGlpiSecKey() {
      $glpikey = new GLPIKey();
      return $glpikey->get();
   }

               CURLOPT_PROXYUSERPWD => $CFG_GLPI["proxy_user"] . ":" . self::sodiumDecrypt($CFG_GLPI["proxy_passwd"]),
         echo 'Upgrade from command line is not supported before 0.72.3!';
         die(1);

      //generate security key if missing, and update db
      $glpikey = new GLPIKey();
      if (!$glpikey->keyExists() && !$glpikey->generate()) {
         $this->migration->displayWarning(__('Unable to create security key file!'), true);
      }
   //create security key
   $glpikey = new GLPIKey();
   $secured = $glpikey->keyExists();
   if (!$secured) {
      $secured = $glpikey->generate();
   }

   if (!$secured) {
      echo "<p><strong>".__('Security key cannot be generated!')."</strong></p>";
      prev_form($host, $user, $password);
      return;
   }


                         SET `proxy_passwd` = '".addslashes(Toolbox::encrypt($value))."'
                         SET `smtp_passwd` = '".addslashes(Toolbox::encrypt($value))."'
                            SET `rootdn_passwd` = '".addslashes(Toolbox::encrypt($data['rootdn_password']))."'
                            SET `passwd` = '".addslashes(Toolbox::encrypt($data['password']))."'
   /** Appliances & webapps */
   require __DIR__ . '/update_94_95/appliances.php';
   /** /Appliances & webapps */

   // ************ Keep it at the end **************
   foreach ($ADDTODISPLAYPREF as $type => $tab) {
      $rank = 1;
      foreach ($tab as $newval) {
         $DB->updateOrInsert("glpi_displaypreferences", [
            'rank'      => $rank++
         ], [
            'users_id'  => "0",
            'itemtype'  => $type,
            'num'       => $newval,
         ]);
      }
   }

                                            Toolbox::sodiumDecrypt($method["rootdn_passwd"]))) {

   public function testPassword() {
      $ldap = new \AuthLDAP();
      $id = (int)$ldap->add([
         'name'        => 'LDAPcrypted',
         'is_active'   => 1,
         'is_default'  => 0,
         'basedn'      => 'ou=people,dc=mycompany',
         'login_field' => 'uid',
         'phone_field' => 'phonenumber'
      ]);
      $this->integer($id)->isGreaterThan(0);

      //rootdn_passwd is set with a value (a password, not encrypted)
      $password = 'toto';
      $input    = ['id' => $id, 'name' => 'ldap', 'rootdn_passwd' => $password];
      $this->boolean($ldap->update($input))->isTrue();
      $this->boolean($ldap->getFromDB($id))->isTrue();

      //Expected value to be encrypted using current  key
      $this->string(\Toolbox::sodiumDecrypt($ldap->fields['rootdn_passwd']))->isIdenticalTo($password);

      $password = 'tot\'o';
      $input    = ['id' => $id, 'name' => 'ldap', 'rootdn_passwd' => $password];
      $this->boolean($ldap->update($input))->isTrue();
      $this->boolean($ldap->getFromDB($id))->isTrue();

      //Expected value to be encrypted using current key
      $this->string(\Toolbox::sodiumDecrypt($ldap->fields['rootdn_passwd']))->isIdenticalTo($password);

      $password = 'tot\'o';
      $input    = ['id' => $id, 'name' => 'ldap', 'rootdn_passwd' => \Toolbox::addslashes_deep($password)];
      $this->boolean($ldap->update($input))->isTrue();
      $this->boolean($ldap->getFromDB($id))->isTrue();

      //Expected value to be encrypted using current key
      $this->string(\Toolbox::sodiumDecrypt($ldap->fields['rootdn_passwd']))->isIdenticalTo($password);

      $input['_blank_passwd'] = 1;
      $result   = $ldap->prepareInputForUpdate($input);
      //rootdn_passwd is set but empty
      $this->string($result['rootdn_passwd'])->isEmpty();

   }
            'passwd'    => \Toolbox::sodiumEncrypt($oinput["passwd"]),
}
   protected function sodiumEncryptProvider() {
      return [
         ['My string'],
         ['keepmysecret'],
         ['This is a strng I want to crypt, with some unusual chars like %, \', @, and so on!']
      ];
    * @dataProvider sodiumEncryptProvider
   public function testSodiumEncrypt($string) {
      $crypted = \Toolbox::sodiumEncrypt($string);
      $this->string($crypted)->isNotEmpty();
      $this->string(\Toolbox::sodiumDecrypt($crypted))->isIdenticalTo($string);