[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

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

   1  <?php
   2  
   3  namespace Defuse\Crypto;
   4  
   5  use Defuse\Crypto\Exception as Ex;
   6  
   7  final class Core
   8  {
   9      const HEADER_VERSION_SIZE               = 4;
  10      const MINIMUM_CIPHERTEXT_SIZE           = 84;
  11  
  12      const CURRENT_VERSION                   = "\xDE\xF5\x02\x00";
  13  
  14      const CIPHER_METHOD                     = 'aes-256-ctr';
  15      const BLOCK_BYTE_SIZE                   = 16;
  16      const KEY_BYTE_SIZE                     = 32;
  17      const SALT_BYTE_SIZE                    = 32;
  18      const MAC_BYTE_SIZE                     = 32;
  19      const HASH_FUNCTION_NAME                = 'sha256';
  20      const ENCRYPTION_INFO_STRING            = 'DefusePHP|V2|KeyForEncryption';
  21      const AUTHENTICATION_INFO_STRING        = 'DefusePHP|V2|KeyForAuthentication';
  22      const BUFFER_BYTE_SIZE                  = 1048576;
  23  
  24      const LEGACY_CIPHER_METHOD              = 'aes-128-cbc';
  25      const LEGACY_BLOCK_BYTE_SIZE            = 16;
  26      const LEGACY_KEY_BYTE_SIZE              = 16;
  27      const LEGACY_HASH_FUNCTION_NAME         = 'sha256';
  28      const LEGACY_MAC_BYTE_SIZE              = 32;
  29      const LEGACY_ENCRYPTION_INFO_STRING     = 'DefusePHP|KeyForEncryption';
  30      const LEGACY_AUTHENTICATION_INFO_STRING = 'DefusePHP|KeyForAuthentication';
  31  
  32      /*
  33       * V2.0 Format: VERSION (4 bytes) || SALT (32 bytes) || IV (16 bytes) ||
  34       *              CIPHERTEXT (varies) || HMAC (32 bytes)
  35       *
  36       * V1.0 Format: HMAC (32 bytes) || IV (16 bytes) || CIPHERTEXT (varies).
  37       */
  38  
  39      /**
  40       * Adds an integer to a block-sized counter.
  41       *
  42       * @param string $ctr
  43       * @param int    $inc
  44       *
  45       * @throws Ex\EnvironmentIsBrokenException
  46       *
  47       * @return string
  48       *
  49       * @psalm-suppress RedundantCondition - It's valid to use is_int to check for overflow.
  50       */
  51      public static function incrementCounter($ctr, $inc)
  52      {
  53          Core::ensureTrue(
  54              Core::ourStrlen($ctr) === Core::BLOCK_BYTE_SIZE,
  55              'Trying to increment a nonce of the wrong size.'
  56          );
  57  
  58          Core::ensureTrue(
  59              \is_int($inc),
  60              'Trying to increment nonce by a non-integer.'
  61          );
  62  
  63          // The caller is probably re-using CTR-mode keystream if they increment by 0.
  64          Core::ensureTrue(
  65              $inc > 0,
  66              'Trying to increment a nonce by a nonpositive amount'
  67          );
  68  
  69          Core::ensureTrue(
  70              $inc <= PHP_INT_MAX - 255,
  71              'Integer overflow may occur'
  72          );
  73  
  74          /*
  75           * We start at the rightmost byte (big-endian)
  76           * So, too, does OpenSSL: http://stackoverflow.com/a/3146214/2224584
  77           */
  78          for ($i = Core::BLOCK_BYTE_SIZE - 1; $i >= 0; --$i) {
  79              $sum = \ord($ctr[$i]) + $inc;
  80  
  81              /* Detect integer overflow and fail. */
  82              Core::ensureTrue(\is_int($sum), 'Integer overflow in CTR mode nonce increment');
  83  
  84              $ctr[$i] = \pack('C', $sum & 0xFF);
  85              $inc     = $sum >> 8;
  86          }
  87          return $ctr;
  88      }
  89  
  90      /**
  91       * Returns a random byte string of the specified length.
  92       *
  93       * @param int $octets
  94       *
  95       * @throws Ex\EnvironmentIsBrokenException
  96       *
  97       * @return string
  98       */
  99      public static function secureRandom($octets)
 100      {
 101          self::ensureFunctionExists('random_bytes');
 102          try {
 103              return \random_bytes($octets);
 104          } catch (\Exception $ex) {
 105              throw new Ex\EnvironmentIsBrokenException(
 106                  'Your system does not have a secure random number generator.'
 107              );
 108          }
 109      }
 110  
 111      /**
 112       * Computes the HKDF key derivation function specified in
 113       * http://tools.ietf.org/html/rfc5869.
 114       *
 115       * @param string $hash   Hash Function
 116       * @param string $ikm    Initial Keying Material
 117       * @param int    $length How many bytes?
 118       * @param string $info   What sort of key are we deriving?
 119       * @param string $salt
 120       *
 121       * @throws Ex\EnvironmentIsBrokenException
 122       * @psalm-suppress UndefinedFunction - We're checking if the function exists first.
 123       *
 124       * @return string
 125       */
 126      public static function HKDF($hash, $ikm, $length, $info = '', $salt = null)
 127      {
 128          static $nativeHKDF = null;
 129          if ($nativeHKDF === null) {
 130              $nativeHKDF = \is_callable('\\hash_hkdf');
 131          }
 132          if ($nativeHKDF) {
 133              if (\is_null($salt)) {
 134                  $salt = '';
 135              }
 136              return \hash_hkdf($hash, $ikm, $length, $info, $salt);
 137          }
 138  
 139          $digest_length = Core::ourStrlen(\hash_hmac($hash, '', '', true));
 140  
 141          // Sanity-check the desired output length.
 142          Core::ensureTrue(
 143              !empty($length) && \is_int($length) && $length >= 0 && $length <= 255 * $digest_length,
 144              'Bad output length requested of HDKF.'
 145          );
 146  
 147          // "if [salt] not provided, is set to a string of HashLen zeroes."
 148          if (\is_null($salt)) {
 149              $salt = \str_repeat("\x00", $digest_length);
 150          }
 151  
 152          // HKDF-Extract:
 153          // PRK = HMAC-Hash(salt, IKM)
 154          // The salt is the HMAC key.
 155          $prk = \hash_hmac($hash, $ikm, $salt, true);
 156  
 157          // HKDF-Expand:
 158  
 159          // This check is useless, but it serves as a reminder to the spec.
 160          Core::ensureTrue(Core::ourStrlen($prk) >= $digest_length);
 161  
 162          // T(0) = ''
 163          $t          = '';
 164          $last_block = '';
 165          for ($block_index = 1; Core::ourStrlen($t) < $length; ++$block_index) {
 166              // T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??)
 167              $last_block = \hash_hmac(
 168                  $hash,
 169                  $last_block . $info . \chr($block_index),
 170                  $prk,
 171                  true
 172              );
 173              // T = T(1) | T(2) | T(3) | ... | T(N)
 174              $t .= $last_block;
 175          }
 176  
 177          // ORM = first L octets of T
 178          /** @var string $orm */
 179          $orm = Core::ourSubstr($t, 0, $length);
 180          Core::ensureTrue(\is_string($orm));
 181          return $orm;
 182      }
 183  
 184      /**
 185       * Checks if two equal-length strings are the same without leaking
 186       * information through side channels.
 187       *
 188       * @param string $expected
 189       * @param string $given
 190       *
 191       * @throws Ex\EnvironmentIsBrokenException
 192       *
 193       * @return bool
 194       */
 195      public static function hashEquals($expected, $given)
 196      {
 197          static $native = null;
 198          if ($native === null) {
 199              $native = \function_exists('hash_equals');
 200          }
 201          if ($native) {
 202              return \hash_equals($expected, $given);
 203          }
 204  
 205          // We can't just compare the strings with '==', since it would make
 206          // timing attacks possible. We could use the XOR-OR constant-time
 207          // comparison algorithm, but that may not be a reliable defense in an
 208          // interpreted language. So we use the approach of HMACing both strings
 209          // with a random key and comparing the HMACs.
 210  
 211          // We're not attempting to make variable-length string comparison
 212          // secure, as that's very difficult. Make sure the strings are the same
 213          // length.
 214          Core::ensureTrue(Core::ourStrlen($expected) === Core::ourStrlen($given));
 215  
 216          $blind           = Core::secureRandom(32);
 217          $message_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $given, $blind);
 218          $correct_compare = \hash_hmac(Core::HASH_FUNCTION_NAME, $expected, $blind);
 219          return $correct_compare === $message_compare;
 220      }
 221      /**
 222       * Throws an exception if the constant doesn't exist.
 223       *
 224       * @param string $name
 225       * @return void
 226       *
 227       * @throws Ex\EnvironmentIsBrokenException
 228       */
 229      public static function ensureConstantExists($name)
 230      {
 231          Core::ensureTrue(
 232              \defined($name),
 233              'Constant '.$name.' does not exists'
 234          );
 235      }
 236  
 237      /**
 238       * Throws an exception if the function doesn't exist.
 239       *
 240       * @param string $name
 241       * @return void
 242       *
 243       * @throws Ex\EnvironmentIsBrokenException
 244       */
 245      public static function ensureFunctionExists($name)
 246      {
 247          Core::ensureTrue(
 248              \function_exists($name),
 249              'function '.$name.' does not exists'
 250          );
 251      }
 252  
 253      /**
 254       * Throws an exception if the condition is false.
 255       *
 256       * @param bool $condition
 257       * @param string $message
 258       * @return void
 259       *
 260       * @throws Ex\EnvironmentIsBrokenException
 261       */
 262      public static function ensureTrue($condition, $message = '')
 263      {
 264          if (!$condition) {
 265              throw new Ex\EnvironmentIsBrokenException($message);
 266          }
 267      }
 268  
 269      /*
 270       * We need these strlen() and substr() functions because when
 271       * 'mbstring.func_overload' is set in php.ini, the standard strlen() and
 272       * substr() are replaced by mb_strlen() and mb_substr().
 273       */
 274  
 275      /**
 276       * Computes the length of a string in bytes.
 277       *
 278       * @param string $str
 279       *
 280       * @throws Ex\EnvironmentIsBrokenException
 281       *
 282       * @return int
 283       */
 284      public static function ourStrlen($str)
 285      {
 286          static $exists = null;
 287          if ($exists === null) {
 288              $exists = \extension_loaded('mbstring') && \ini_get('mbstring.func_overload') !== false && (int)\ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING;
 289          }
 290          if ($exists) {
 291              $length = \mb_strlen($str, '8bit');
 292              Core::ensureTrue($length !== false);
 293              return $length;
 294          } else {
 295              return \strlen($str);
 296          }
 297      }
 298  
 299      /**
 300       * Behaves roughly like the function substr() in PHP 7 does.
 301       *
 302       * @param string $str
 303       * @param int    $start
 304       * @param int    $length
 305       *
 306       * @throws Ex\EnvironmentIsBrokenException
 307       *
 308       * @return string|bool
 309       */
 310      public static function ourSubstr($str, $start, $length = null)
 311      {
 312          static $exists = null;
 313          if ($exists === null) {
 314              $exists = \extension_loaded('mbstring') && \ini_get('mbstring.func_overload') !== false && (int)\ini_get('mbstring.func_overload') & MB_OVERLOAD_STRING;
 315          }
 316  
 317          // This is required to make mb_substr behavior identical to substr.
 318          // Without this, mb_substr() would return false, contra to what the
 319          // PHP documentation says (it doesn't say it can return false.)
 320          $input_len = Core::ourStrlen($str);
 321          if ($start === $input_len && !$length) {
 322              return '';
 323          }
 324  
 325          if ($start > $input_len) {
 326              return false;
 327          }
 328  
 329          // mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP 5.3,
 330          // so we have to find the length ourselves. Also, substr() doesn't
 331          // accept null for the length.
 332          if (! isset($length)) {
 333              if ($start >= 0) {
 334                  $length = $input_len - $start;
 335              } else {
 336                  $length = -$start;
 337              }
 338          }
 339  
 340          if ($length < 0) {
 341              throw new \InvalidArgumentException(
 342                  "Negative lengths are not supported with ourSubstr."
 343              );
 344          }
 345  
 346          if ($exists) {
 347              $substr = \mb_substr($str, $start, $length, '8bit');
 348              // At this point there are two cases where mb_substr can
 349              // legitimately return an empty string. Either $length is 0, or
 350              // $start is equal to the length of the string (both mb_substr and
 351              // substr return an empty string when this happens). It should never
 352              // ever return a string that's longer than $length.
 353              if (Core::ourStrlen($substr) > $length || (Core::ourStrlen($substr) === 0 && $length !== 0 && $start !== $input_len)) {
 354                  throw new Ex\EnvironmentIsBrokenException(
 355                      'Your version of PHP has bug #66797. Its implementation of
 356                      mb_substr() is incorrect. See the details here:
 357                      https://bugs.php.net/bug.php?id=66797'
 358                  );
 359              }
 360              return $substr;
 361          }
 362  
 363          return \substr($str, $start, $length);
 364      }
 365  
 366      /**
 367       * Computes the PBKDF2 password-based key derivation function.
 368       *
 369       * The PBKDF2 function is defined in RFC 2898. Test vectors can be found in
 370       * RFC 6070. This implementation of PBKDF2 was originally created by Taylor
 371       * Hornby, with improvements from http://www.variations-of-shadow.com/.
 372       *
 373       * @param string $algorithm  The hash algorithm to use. Recommended: SHA256
 374       * @param string $password   The password.
 375       * @param string $salt       A salt that is unique to the password.
 376       * @param int    $count      Iteration count. Higher is better, but slower. Recommended: At least 1000.
 377       * @param int    $key_length The length of the derived key in bytes.
 378       * @param bool   $raw_output If true, the key is returned in raw binary format. Hex encoded otherwise.
 379       *
 380       * @throws Ex\EnvironmentIsBrokenException
 381       *
 382       * @return string A $key_length-byte key derived from the password and salt.
 383       */
 384      public static function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)
 385      {
 386          // Type checks:
 387          if (! \is_string($algorithm)) {
 388              throw new \InvalidArgumentException(
 389                  'pbkdf2(): algorithm must be a string'
 390              );
 391          }
 392          if (! \is_string($password)) {
 393              throw new \InvalidArgumentException(
 394                  'pbkdf2(): password must be a string'
 395              );
 396          }
 397          if (! \is_string($salt)) {
 398              throw new \InvalidArgumentException(
 399                  'pbkdf2(): salt must be a string'
 400              );
 401          }
 402          // Coerce strings to integers with no information loss or overflow
 403          $count += 0;
 404          $key_length += 0;
 405  
 406          $algorithm = \strtolower($algorithm);
 407          Core::ensureTrue(
 408              \in_array($algorithm, \hash_algos(), true),
 409              'Invalid or unsupported hash algorithm.'
 410          );
 411  
 412          // Whitelist, or we could end up with people using CRC32.
 413          $ok_algorithms = [
 414              'sha1', 'sha224', 'sha256', 'sha384', 'sha512',
 415              'ripemd160', 'ripemd256', 'ripemd320', 'whirlpool',
 416          ];
 417          Core::ensureTrue(
 418              \in_array($algorithm, $ok_algorithms, true),
 419              'Algorithm is not a secure cryptographic hash function.'
 420          );
 421  
 422          Core::ensureTrue($count > 0 && $key_length > 0, 'Invalid PBKDF2 parameters.');
 423  
 424          if (\function_exists('hash_pbkdf2')) {
 425              // The output length is in NIBBLES (4-bits) if $raw_output is false!
 426              if (! $raw_output) {
 427                  $key_length = $key_length * 2;
 428              }
 429              return \hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
 430          }
 431  
 432          $hash_length = Core::ourStrlen(\hash($algorithm, '', true));
 433          $block_count = \ceil($key_length / $hash_length);
 434  
 435          $output = '';
 436          for ($i = 1; $i <= $block_count; $i++) {
 437              // $i encoded as 4 bytes, big endian.
 438              $last = $salt . \pack('N', $i);
 439              // first iteration
 440              $last = $xorsum = \hash_hmac($algorithm, $last, $password, true);
 441              // perform the other $count - 1 iterations
 442              for ($j = 1; $j < $count; $j++) {
 443                  /**
 444                   * @psalm-suppress InvalidOperand
 445                   */
 446                  $xorsum ^= ($last = \hash_hmac($algorithm, $last, $password, true));
 447              }
 448              $output .= $xorsum;
 449          }
 450  
 451          if ($raw_output) {
 452              return (string) Core::ourSubstr($output, 0, $key_length);
 453          } else {
 454              return Encoding::binToHex((string) Core::ourSubstr($output, 0, $key_length));
 455          }
 456      }
 457  }


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