[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/defuse/php-encryption/src/ -> Crypto.php (source)

   1  <?php
   2  
   3  namespace Defuse\Crypto;
   4  
   5  use Defuse\Crypto\Exception as Ex;
   6  
   7  class Crypto
   8  {
   9      /**
  10       * Encrypts a string with a Key.
  11       *
  12       * @param string $plaintext
  13       * @param Key    $key
  14       * @param bool   $raw_binary
  15       *
  16       * @throws Ex\EnvironmentIsBrokenException
  17       * @throws \TypeError
  18       *
  19       * @return string
  20       */
  21      public static function encrypt($plaintext, $key, $raw_binary = false)
  22      {
  23          if (!\is_string($plaintext)) {
  24              throw new \TypeError(
  25                  'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.'
  26              );
  27          }
  28          if (!($key instanceof Key)) {
  29              throw new \TypeError(
  30                  'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
  31              );
  32          }
  33          if (!\is_bool($raw_binary)) {
  34              throw new \TypeError(
  35                  'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
  36              );
  37          }
  38          return self::encryptInternal(
  39              $plaintext,
  40              KeyOrPassword::createFromKey($key),
  41              $raw_binary
  42          );
  43      }
  44  
  45      /**
  46       * Encrypts a string with a password, using a slow key derivation function
  47       * to make password cracking more expensive.
  48       *
  49       * @param string $plaintext
  50       * @param string $password
  51       * @param bool   $raw_binary
  52       *
  53       * @throws Ex\EnvironmentIsBrokenException
  54       * @throws \TypeError
  55       *
  56       * @return string
  57       */
  58      public static function encryptWithPassword($plaintext, $password, $raw_binary = false)
  59      {
  60          if (!\is_string($plaintext)) {
  61              throw new \TypeError(
  62                  'String expected for argument 1. ' . \ucfirst(\gettype($plaintext)) . ' given instead.'
  63              );
  64          }
  65          if (!\is_string($password)) {
  66              throw new \TypeError(
  67                  'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.'
  68              );
  69          }
  70          if (!\is_bool($raw_binary)) {
  71              throw new \TypeError(
  72                  'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
  73              );
  74          }
  75          return self::encryptInternal(
  76              $plaintext,
  77              KeyOrPassword::createFromPassword($password),
  78              $raw_binary
  79          );
  80      }
  81  
  82      /**
  83       * Decrypts a ciphertext to a string with a Key.
  84       *
  85       * @param string $ciphertext
  86       * @param Key    $key
  87       * @param bool   $raw_binary
  88       *
  89       * @throws \TypeError
  90       * @throws Ex\EnvironmentIsBrokenException
  91       * @throws Ex\WrongKeyOrModifiedCiphertextException
  92       *
  93       * @return string
  94       */
  95      public static function decrypt($ciphertext, $key, $raw_binary = false)
  96      {
  97          if (!\is_string($ciphertext)) {
  98              throw new \TypeError(
  99                  'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
 100              );
 101          }
 102          if (!($key instanceof Key)) {
 103              throw new \TypeError(
 104                  'Key expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
 105              );
 106          }
 107          if (!\is_bool($raw_binary)) {
 108              throw new \TypeError(
 109                  'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
 110              );
 111          }
 112          return self::decryptInternal(
 113              $ciphertext,
 114              KeyOrPassword::createFromKey($key),
 115              $raw_binary
 116          );
 117      }
 118  
 119      /**
 120       * Decrypts a ciphertext to a string with a password, using a slow key
 121       * derivation function to make password cracking more expensive.
 122       *
 123       * @param string $ciphertext
 124       * @param string $password
 125       * @param bool   $raw_binary
 126       *
 127       * @throws Ex\EnvironmentIsBrokenException
 128       * @throws Ex\WrongKeyOrModifiedCiphertextException
 129       * @throws \TypeError
 130       *
 131       * @return string
 132       */
 133      public static function decryptWithPassword($ciphertext, $password, $raw_binary = false)
 134      {
 135          if (!\is_string($ciphertext)) {
 136              throw new \TypeError(
 137                  'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
 138              );
 139          }
 140          if (!\is_string($password)) {
 141              throw new \TypeError(
 142                  'String expected for argument 2. ' . \ucfirst(\gettype($password)) . ' given instead.'
 143              );
 144          }
 145          if (!\is_bool($raw_binary)) {
 146              throw new \TypeError(
 147                  'Boolean expected for argument 3. ' . \ucfirst(\gettype($raw_binary)) . ' given instead.'
 148              );
 149          }
 150          return self::decryptInternal(
 151              $ciphertext,
 152              KeyOrPassword::createFromPassword($password),
 153              $raw_binary
 154          );
 155      }
 156  
 157      /**
 158       * Decrypts a legacy ciphertext produced by version 1 of this library.
 159       *
 160       * @param string $ciphertext
 161       * @param string $key
 162       *
 163       * @throws Ex\EnvironmentIsBrokenException
 164       * @throws Ex\WrongKeyOrModifiedCiphertextException
 165       * @throws \TypeError
 166       *
 167       * @return string
 168       */
 169      public static function legacyDecrypt($ciphertext, $key)
 170      {
 171          if (!\is_string($ciphertext)) {
 172              throw new \TypeError(
 173                  'String expected for argument 1. ' . \ucfirst(\gettype($ciphertext)) . ' given instead.'
 174              );
 175          }
 176          if (!\is_string($key)) {
 177              throw new \TypeError(
 178                  'String expected for argument 2. ' . \ucfirst(\gettype($key)) . ' given instead.'
 179              );
 180          }
 181  
 182          RuntimeTests::runtimeTest();
 183  
 184          // Extract the HMAC from the front of the ciphertext.
 185          if (Core::ourStrlen($ciphertext) <= Core::LEGACY_MAC_BYTE_SIZE) {
 186              throw new Ex\WrongKeyOrModifiedCiphertextException(
 187                  'Ciphertext is too short.'
 188              );
 189          }
 190          /**
 191           * @var string
 192           */
 193          $hmac = Core::ourSubstr($ciphertext, 0, Core::LEGACY_MAC_BYTE_SIZE);
 194          Core::ensureTrue(\is_string($hmac));
 195          /**
 196           * @var string
 197           */
 198          $messageCiphertext = Core::ourSubstr($ciphertext, Core::LEGACY_MAC_BYTE_SIZE);
 199          Core::ensureTrue(\is_string($messageCiphertext));
 200  
 201          // Regenerate the same authentication sub-key.
 202          $akey = Core::HKDF(
 203              Core::LEGACY_HASH_FUNCTION_NAME,
 204              $key,
 205              Core::LEGACY_KEY_BYTE_SIZE,
 206              Core::LEGACY_AUTHENTICATION_INFO_STRING,
 207              null
 208          );
 209  
 210          if (self::verifyHMAC($hmac, $messageCiphertext, $akey)) {
 211              // Regenerate the same encryption sub-key.
 212              $ekey = Core::HKDF(
 213                  Core::LEGACY_HASH_FUNCTION_NAME,
 214                  $key,
 215                  Core::LEGACY_KEY_BYTE_SIZE,
 216                  Core::LEGACY_ENCRYPTION_INFO_STRING,
 217                  null
 218              );
 219  
 220              // Extract the IV from the ciphertext.
 221              if (Core::ourStrlen($messageCiphertext) <= Core::LEGACY_BLOCK_BYTE_SIZE) {
 222                  throw new Ex\WrongKeyOrModifiedCiphertextException(
 223                      'Ciphertext is too short.'
 224                  );
 225              }
 226              /**
 227               * @var string
 228               */
 229              $iv = Core::ourSubstr($messageCiphertext, 0, Core::LEGACY_BLOCK_BYTE_SIZE);
 230              Core::ensureTrue(\is_string($iv));
 231  
 232              /**
 233               * @var string
 234               */
 235              $actualCiphertext = Core::ourSubstr($messageCiphertext, Core::LEGACY_BLOCK_BYTE_SIZE);
 236              Core::ensureTrue(\is_string($actualCiphertext));
 237  
 238              // Do the decryption.
 239              $plaintext = self::plainDecrypt($actualCiphertext, $ekey, $iv, Core::LEGACY_CIPHER_METHOD);
 240              return $plaintext;
 241          } else {
 242              throw new Ex\WrongKeyOrModifiedCiphertextException(
 243                  'Integrity check failed.'
 244              );
 245          }
 246      }
 247  
 248      /**
 249       * Encrypts a string with either a key or a password.
 250       *
 251       * @param string        $plaintext
 252       * @param KeyOrPassword $secret
 253       * @param bool          $raw_binary
 254       *
 255       * @return string
 256       */
 257      private static function encryptInternal($plaintext, KeyOrPassword $secret, $raw_binary)
 258      {
 259          RuntimeTests::runtimeTest();
 260  
 261          $salt = Core::secureRandom(Core::SALT_BYTE_SIZE);
 262          $keys = $secret->deriveKeys($salt);
 263          $ekey = $keys->getEncryptionKey();
 264          $akey = $keys->getAuthenticationKey();
 265          $iv     = Core::secureRandom(Core::BLOCK_BYTE_SIZE);
 266  
 267          $ciphertext = Core::CURRENT_VERSION . $salt . $iv . self::plainEncrypt($plaintext, $ekey, $iv);
 268          $auth       = \hash_hmac(Core::HASH_FUNCTION_NAME, $ciphertext, $akey, true);
 269          $ciphertext = $ciphertext . $auth;
 270  
 271          if ($raw_binary) {
 272              return $ciphertext;
 273          }
 274          return Encoding::binToHex($ciphertext);
 275      }
 276  
 277      /**
 278       * Decrypts a ciphertext to a string with either a key or a password.
 279       *
 280       * @param string        $ciphertext
 281       * @param KeyOrPassword $secret
 282       * @param bool          $raw_binary
 283       *
 284       * @throws Ex\EnvironmentIsBrokenException
 285       * @throws Ex\WrongKeyOrModifiedCiphertextException
 286       *
 287       * @return string
 288       */
 289      private static function decryptInternal($ciphertext, KeyOrPassword $secret, $raw_binary)
 290      {
 291          RuntimeTests::runtimeTest();
 292  
 293          if (! $raw_binary) {
 294              try {
 295                  $ciphertext = Encoding::hexToBin($ciphertext);
 296              } catch (Ex\BadFormatException $ex) {
 297                  throw new Ex\WrongKeyOrModifiedCiphertextException(
 298                      'Ciphertext has invalid hex encoding.'
 299                  );
 300              }
 301          }
 302  
 303          if (Core::ourStrlen($ciphertext) < Core::MINIMUM_CIPHERTEXT_SIZE) {
 304              throw new Ex\WrongKeyOrModifiedCiphertextException(
 305                  'Ciphertext is too short.'
 306              );
 307          }
 308  
 309          // Get and check the version header.
 310          /** @var string $header */
 311          $header = Core::ourSubstr($ciphertext, 0, Core::HEADER_VERSION_SIZE);
 312          if ($header !== Core::CURRENT_VERSION) {
 313              throw new Ex\WrongKeyOrModifiedCiphertextException(
 314                  'Bad version header.'
 315              );
 316          }
 317  
 318          // Get the salt.
 319          /** @var string $salt */
 320          $salt = Core::ourSubstr(
 321              $ciphertext,
 322              Core::HEADER_VERSION_SIZE,
 323              Core::SALT_BYTE_SIZE
 324          );
 325          Core::ensureTrue(\is_string($salt));
 326  
 327          // Get the IV.
 328          /** @var string $iv */
 329          $iv = Core::ourSubstr(
 330              $ciphertext,
 331              Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE,
 332              Core::BLOCK_BYTE_SIZE
 333          );
 334          Core::ensureTrue(\is_string($iv));
 335  
 336          // Get the HMAC.
 337          /** @var string $hmac */
 338          $hmac = Core::ourSubstr(
 339              $ciphertext,
 340              Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE,
 341              Core::MAC_BYTE_SIZE
 342          );
 343          Core::ensureTrue(\is_string($hmac));
 344  
 345          // Get the actual encrypted ciphertext.
 346          /** @var string $encrypted */
 347          $encrypted = Core::ourSubstr(
 348              $ciphertext,
 349              Core::HEADER_VERSION_SIZE + Core::SALT_BYTE_SIZE +
 350                  Core::BLOCK_BYTE_SIZE,
 351              Core::ourStrlen($ciphertext) - Core::MAC_BYTE_SIZE - Core::SALT_BYTE_SIZE -
 352                  Core::BLOCK_BYTE_SIZE - Core::HEADER_VERSION_SIZE
 353          );
 354          Core::ensureTrue(\is_string($encrypted));
 355  
 356          // Derive the separate encryption and authentication keys from the key
 357          // or password, whichever it is.
 358          $keys = $secret->deriveKeys($salt);
 359  
 360          if (self::verifyHMAC($hmac, $header . $salt . $iv . $encrypted, $keys->getAuthenticationKey())) {
 361              $plaintext = self::plainDecrypt($encrypted, $keys->getEncryptionKey(), $iv, Core::CIPHER_METHOD);
 362              return $plaintext;
 363          } else {
 364              throw new Ex\WrongKeyOrModifiedCiphertextException(
 365                  'Integrity check failed.'
 366              );
 367          }
 368      }
 369  
 370      /**
 371       * Raw unauthenticated encryption (insecure on its own).
 372       *
 373       * @param string $plaintext
 374       * @param string $key
 375       * @param string $iv
 376       *
 377       * @throws Ex\EnvironmentIsBrokenException
 378       *
 379       * @return string
 380       */
 381      protected static function plainEncrypt($plaintext, $key, $iv)
 382      {
 383          Core::ensureConstantExists('OPENSSL_RAW_DATA');
 384          Core::ensureFunctionExists('openssl_encrypt');
 385          /** @var string $ciphertext */
 386          $ciphertext = \openssl_encrypt(
 387              $plaintext,
 388              Core::CIPHER_METHOD,
 389              $key,
 390              OPENSSL_RAW_DATA,
 391              $iv
 392          );
 393  
 394          Core::ensureTrue(\is_string($ciphertext), 'openssl_encrypt() failed');
 395  
 396          return $ciphertext;
 397      }
 398  
 399      /**
 400       * Raw unauthenticated decryption (insecure on its own).
 401       *
 402       * @param string $ciphertext
 403       * @param string $key
 404       * @param string $iv
 405       * @param string $cipherMethod
 406       *
 407       * @throws Ex\EnvironmentIsBrokenException
 408       *
 409       * @return string
 410       */
 411      protected static function plainDecrypt($ciphertext, $key, $iv, $cipherMethod)
 412      {
 413          Core::ensureConstantExists('OPENSSL_RAW_DATA');
 414          Core::ensureFunctionExists('openssl_decrypt');
 415  
 416          /** @var string $plaintext */
 417          $plaintext = \openssl_decrypt(
 418              $ciphertext,
 419              $cipherMethod,
 420              $key,
 421              OPENSSL_RAW_DATA,
 422              $iv
 423          );
 424          Core::ensureTrue(\is_string($plaintext), 'openssl_decrypt() failed.');
 425  
 426          return $plaintext;
 427      }
 428  
 429      /**
 430       * Verifies an HMAC without leaking information through side-channels.
 431       *
 432       * @param string $expected_hmac
 433       * @param string $message
 434       * @param string $key
 435       *
 436       * @throws Ex\EnvironmentIsBrokenException
 437       *
 438       * @return bool
 439       */
 440      protected static function verifyHMAC($expected_hmac, $message, $key)
 441      {
 442          $message_hmac = \hash_hmac(Core::HASH_FUNCTION_NAME, $message, $key, true);
 443          return Core::hashEquals($message_hmac, $expected_hmac);
 444      }
 445  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer