[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/phpseclib/phpseclib/phpseclib/Crypt/ -> Rijndael.php (source)

   1  <?php
   2  
   3  /**
   4   * Pure-PHP implementation of Rijndael.
   5   *
   6   * Uses mcrypt, if available/possible, and an internal implementation, otherwise.
   7   *
   8   * PHP version 5
   9   *
  10   * If {@link self::setBlockLength() setBlockLength()} isn't called, it'll be assumed to be 128 bits.  If
  11   * {@link self::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
  12   * {@link self::setKey() setKey()}.  ie. if the key is 128-bits, the key length will be 128-bits.  If it's
  13   * 136-bits it'll be null-padded to 192-bits and 192 bits will be the key length until
  14   * {@link self::setKey() setKey()} is called, again, at which point, it'll be recalculated.
  15   *
  16   * Not all Rijndael implementations may support 160-bits or 224-bits as the block length / key length.  mcrypt, for example,
  17   * does not.  AES, itself, only supports block lengths of 128 and key lengths of 128, 192, and 256.
  18   * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=10 Rijndael-ammended.pdf#page=10} defines the
  19   * algorithm for block lengths of 192 and 256 but not for block lengths / key lengths of 160 and 224.  Indeed, 160 and 224
  20   * are first defined as valid key / block lengths in
  21   * {@link http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=44 Rijndael-ammended.pdf#page=44}:
  22   * Extensions: Other block and Cipher Key lengths.
  23   * Note: Use of 160/224-bit Keys must be explicitly set by setKeyLength(160) respectively setKeyLength(224).
  24   *
  25   * {@internal The variable names are the same as those in
  26   * {@link http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf#page=10 fips-197.pdf#page=10}.}}
  27   *
  28   * Here's a short example of how to use this library:
  29   * <code>
  30   * <?php
  31   *    include 'vendor/autoload.php';
  32   *
  33   *    $rijndael = new \phpseclib3\Crypt\Rijndael('ctr');
  34   *
  35   *    $rijndael->setKey('abcdefghijklmnop');
  36   *
  37   *    $size = 10 * 1024;
  38   *    $plaintext = '';
  39   *    for ($i = 0; $i < $size; $i++) {
  40   *        $plaintext.= 'a';
  41   *    }
  42   *
  43   *    echo $rijndael->decrypt($rijndael->encrypt($plaintext));
  44   * ?>
  45   * </code>
  46   *
  47   * @author    Jim Wigginton <terrafrost@php.net>
  48   * @copyright 2008 Jim Wigginton
  49   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  50   * @link      http://phpseclib.sourceforge.net
  51   */
  52  
  53  namespace phpseclib3\Crypt;
  54  
  55  use phpseclib3\Common\Functions\Strings;
  56  use phpseclib3\Crypt\Common\BlockCipher;
  57  use phpseclib3\Exception\BadDecryptionException;
  58  use phpseclib3\Exception\BadModeException;
  59  use phpseclib3\Exception\InconsistentSetupException;
  60  use phpseclib3\Exception\InsufficientSetupException;
  61  
  62  /**
  63   * Pure-PHP implementation of Rijndael.
  64   *
  65   * @author  Jim Wigginton <terrafrost@php.net>
  66   */
  67  class Rijndael extends BlockCipher
  68  {
  69      /**
  70       * The mcrypt specific name of the cipher
  71       *
  72       * Mcrypt is useable for 128/192/256-bit $block_size/$key_length. For 160/224 not.
  73       * \phpseclib3\Crypt\Rijndael determines automatically whether mcrypt is useable
  74       * or not for the current $block_size/$key_length.
  75       * In case of, $cipher_name_mcrypt will be set dynamically at run time accordingly.
  76       *
  77       * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
  78       * @see \phpseclib3\Crypt\Common\SymmetricKey::engine
  79       * @see self::isValidEngine()
  80       * @var string
  81       */
  82      protected $cipher_name_mcrypt = 'rijndael-128';
  83  
  84      /**
  85       * The Key Schedule
  86       *
  87       * @see self::setup()
  88       * @var array
  89       */
  90      private $w;
  91  
  92      /**
  93       * The Inverse Key Schedule
  94       *
  95       * @see self::setup()
  96       * @var array
  97       */
  98      private $dw;
  99  
 100      /**
 101       * The Block Length divided by 32
 102       *
 103       * {@internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4.  Exists in conjunction with $block_size
 104       *    because the encryption / decryption / key schedule creation requires this number and not $block_size.  We could
 105       *    derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
 106       *    of that, we'll just precompute it once.}
 107       *
 108       * @see self::setBlockLength()
 109       * @var int
 110       */
 111      private $Nb = 4;
 112  
 113      /**
 114       * The Key Length (in bytes)
 115       *
 116       * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16.  Exists in conjunction with $Nk
 117       *    because the encryption / decryption / key schedule creation requires this number and not $key_length.  We could
 118       *    derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
 119       *    of that, we'll just precompute it once.}
 120       *
 121       * @see self::setKeyLength()
 122       * @var int
 123       */
 124      protected $key_length = 16;
 125  
 126      /**
 127       * The Key Length divided by 32
 128       *
 129       * @see self::setKeyLength()
 130       * @var int
 131       * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
 132       */
 133      private $Nk = 4;
 134  
 135      /**
 136       * The Number of Rounds
 137       *
 138       * {@internal The max value is 14, the min value is 10.}
 139       *
 140       * @var int
 141       */
 142      private $Nr;
 143  
 144      /**
 145       * Shift offsets
 146       *
 147       * @var array
 148       */
 149      private $c;
 150  
 151      /**
 152       * Holds the last used key- and block_size information
 153       *
 154       * @var array
 155       */
 156      private $kl;
 157  
 158      /**
 159       * Default Constructor.
 160       *
 161       * @param string $mode
 162       * @throws \InvalidArgumentException if an invalid / unsupported mode is provided
 163       */
 164      public function __construct($mode)
 165      {
 166          parent::__construct($mode);
 167  
 168          if ($this->mode == self::MODE_STREAM) {
 169              throw new BadModeException('Block ciphers cannot be ran in stream mode');
 170          }
 171      }
 172  
 173      /**
 174       * Sets the key length.
 175       *
 176       * Valid key lengths are 128, 160, 192, 224, and 256.
 177       *
 178       * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
 179       *       and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
 180       *       192/256 bits as, for example, mcrypt will do.
 181       *
 182       *       That said, if you want be compatible with other Rijndael and AES implementations,
 183       *       you should not setKeyLength(160) or setKeyLength(224).
 184       *
 185       * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use
 186       *             the mcrypt php extension, even if available.
 187       *             This results then in slower encryption.
 188       *
 189       * @throws \LengthException if the key length is invalid
 190       * @param int $length
 191       */
 192      public function setKeyLength($length)
 193      {
 194          switch ($length) {
 195              case 128:
 196              case 160:
 197              case 192:
 198              case 224:
 199              case 256:
 200                  $this->key_length = $length >> 3;
 201                  break;
 202              default:
 203                  throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
 204          }
 205  
 206          parent::setKeyLength($length);
 207      }
 208  
 209      /**
 210       * Sets the key.
 211       *
 212       * Rijndael supports five different key lengths
 213       *
 214       * @see setKeyLength()
 215       * @param string $key
 216       * @throws \LengthException if the key length isn't supported
 217       */
 218      public function setKey($key)
 219      {
 220          switch (strlen($key)) {
 221              case 16:
 222              case 20:
 223              case 24:
 224              case 28:
 225              case 32:
 226                  break;
 227              default:
 228                  throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16, 20, 24, 28 or 32 are supported');
 229          }
 230  
 231          parent::setKey($key);
 232      }
 233  
 234      /**
 235       * Sets the block length
 236       *
 237       * Valid block lengths are 128, 160, 192, 224, and 256.
 238       *
 239       * @param int $length
 240       */
 241      public function setBlockLength($length)
 242      {
 243          switch ($length) {
 244              case 128:
 245              case 160:
 246              case 192:
 247              case 224:
 248              case 256:
 249                  break;
 250              default:
 251                  throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes 128, 160, 192, 224 or 256 bits are supported');
 252          }
 253  
 254          $this->Nb = $length >> 5;
 255          $this->block_size = $length >> 3;
 256          $this->changed = $this->nonIVChanged = true;
 257          $this->setEngine();
 258      }
 259  
 260      /**
 261       * Test for engine validity
 262       *
 263       * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
 264       *
 265       * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
 266       * @param int $engine
 267       * @return bool
 268       */
 269      protected function isValidEngineHelper($engine)
 270      {
 271          switch ($engine) {
 272              case self::ENGINE_LIBSODIUM:
 273                  return function_exists('sodium_crypto_aead_aes256gcm_is_available') &&
 274                         sodium_crypto_aead_aes256gcm_is_available() &&
 275                         $this->mode == self::MODE_GCM &&
 276                         $this->key_length == 32 &&
 277                         $this->nonce && strlen($this->nonce) == 12 &&
 278                         $this->block_size == 16;
 279              case self::ENGINE_OPENSSL_GCM:
 280                  if (!extension_loaded('openssl')) {
 281                      return false;
 282                  }
 283                  $methods = openssl_get_cipher_methods();
 284                  return $this->mode == self::MODE_GCM &&
 285                         version_compare(PHP_VERSION, '7.1.0', '>=') &&
 286                         in_array('aes-' . $this->getKeyLength() . '-gcm', $methods) &&
 287                         $this->block_size == 16;
 288              case self::ENGINE_OPENSSL:
 289                  if ($this->block_size != 16) {
 290                      return false;
 291                  }
 292                  $this->cipher_name_openssl_ecb = 'aes-' . ($this->key_length << 3) . '-ecb';
 293                  $this->cipher_name_openssl = 'aes-' . ($this->key_length << 3) . '-' . $this->openssl_translate_mode();
 294                  break;
 295              case self::ENGINE_MCRYPT:
 296                  $this->cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
 297                  if ($this->key_length % 8) { // is it a 160/224-bit key?
 298                      // mcrypt is not usable for them, only for 128/192/256-bit keys
 299                      return false;
 300                  }
 301          }
 302  
 303          return parent::isValidEngineHelper($engine);
 304      }
 305  
 306      /**
 307       * Encrypts a block
 308       *
 309       * @param string $in
 310       * @return string
 311       */
 312      protected function encryptBlock($in)
 313      {
 314          static $tables;
 315          if (empty($tables)) {
 316              $tables = &$this->getTables();
 317          }
 318          $t0   = $tables[0];
 319          $t1   = $tables[1];
 320          $t2   = $tables[2];
 321          $t3   = $tables[3];
 322          $sbox = $tables[4];
 323  
 324          $state = [];
 325          $words = unpack('N*', $in);
 326  
 327          $c = $this->c;
 328          $w = $this->w;
 329          $Nb = $this->Nb;
 330          $Nr = $this->Nr;
 331  
 332          // addRoundKey
 333          $wc = $Nb - 1;
 334          foreach ($words as $word) {
 335              $state[] = $word ^ $w[++$wc];
 336          }
 337  
 338          // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
 339          // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
 340          // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
 341          // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
 342          // Unfortunately, the description given there is not quite correct.  Per aes.spec.v316.pdf#page=19 [1],
 343          // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
 344  
 345          // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
 346          $temp = [];
 347          for ($round = 1; $round < $Nr; ++$round) {
 348              $i = 0; // $c[0] == 0
 349              $j = $c[1];
 350              $k = $c[2];
 351              $l = $c[3];
 352  
 353              while ($i < $Nb) {
 354                  $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
 355                              $t1[$state[$j] >> 16 & 0x000000FF] ^
 356                              $t2[$state[$k] >>  8 & 0x000000FF] ^
 357                              $t3[$state[$l]       & 0x000000FF] ^
 358                              $w[++$wc];
 359                  ++$i;
 360                  $j = ($j + 1) % $Nb;
 361                  $k = ($k + 1) % $Nb;
 362                  $l = ($l + 1) % $Nb;
 363              }
 364              $state = $temp;
 365          }
 366  
 367          // subWord
 368          for ($i = 0; $i < $Nb; ++$i) {
 369              $state[$i] =   $sbox[$state[$i]       & 0x000000FF]        |
 370                            ($sbox[$state[$i] >>  8 & 0x000000FF] <<  8) |
 371                            ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
 372                            ($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
 373          }
 374  
 375          // shiftRows + addRoundKey
 376          $i = 0; // $c[0] == 0
 377          $j = $c[1];
 378          $k = $c[2];
 379          $l = $c[3];
 380          while ($i < $Nb) {
 381              $temp[$i] = ($state[$i] & intval(0xFF000000)) ^
 382                          ($state[$j] & 0x00FF0000) ^
 383                          ($state[$k] & 0x0000FF00) ^
 384                          ($state[$l] & 0x000000FF) ^
 385                           $w[$i];
 386              ++$i;
 387              $j = ($j + 1) % $Nb;
 388              $k = ($k + 1) % $Nb;
 389              $l = ($l + 1) % $Nb;
 390          }
 391  
 392          return pack('N*', ...$temp);
 393      }
 394  
 395      /**
 396       * Decrypts a block
 397       *
 398       * @param string $in
 399       * @return string
 400       */
 401      protected function decryptBlock($in)
 402      {
 403          static $invtables;
 404          if (empty($invtables)) {
 405              $invtables = &$this->getInvTables();
 406          }
 407          $dt0   = $invtables[0];
 408          $dt1   = $invtables[1];
 409          $dt2   = $invtables[2];
 410          $dt3   = $invtables[3];
 411          $isbox = $invtables[4];
 412  
 413          $state = [];
 414          $words = unpack('N*', $in);
 415  
 416          $c  = $this->c;
 417          $dw = $this->dw;
 418          $Nb = $this->Nb;
 419          $Nr = $this->Nr;
 420  
 421          // addRoundKey
 422          $wc = $Nb - 1;
 423          foreach ($words as $word) {
 424              $state[] = $word ^ $dw[++$wc];
 425          }
 426  
 427          $temp = [];
 428          for ($round = $Nr - 1; $round > 0; --$round) {
 429              $i = 0; // $c[0] == 0
 430              $j = $Nb - $c[1];
 431              $k = $Nb - $c[2];
 432              $l = $Nb - $c[3];
 433  
 434              while ($i < $Nb) {
 435                  $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
 436                              $dt1[$state[$j] >> 16 & 0x000000FF] ^
 437                              $dt2[$state[$k] >>  8 & 0x000000FF] ^
 438                              $dt3[$state[$l]       & 0x000000FF] ^
 439                              $dw[++$wc];
 440                  ++$i;
 441                  $j = ($j + 1) % $Nb;
 442                  $k = ($k + 1) % $Nb;
 443                  $l = ($l + 1) % $Nb;
 444              }
 445              $state = $temp;
 446          }
 447  
 448          // invShiftRows + invSubWord + addRoundKey
 449          $i = 0; // $c[0] == 0
 450          $j = $Nb - $c[1];
 451          $k = $Nb - $c[2];
 452          $l = $Nb - $c[3];
 453  
 454          while ($i < $Nb) {
 455              $word = ($state[$i] & intval(0xFF000000)) |
 456                      ($state[$j] & 0x00FF0000) |
 457                      ($state[$k] & 0x0000FF00) |
 458                      ($state[$l] & 0x000000FF);
 459  
 460              $temp[$i] = $dw[$i] ^ ($isbox[$word       & 0x000000FF]        |
 461                                    ($isbox[$word >>  8 & 0x000000FF] <<  8) |
 462                                    ($isbox[$word >> 16 & 0x000000FF] << 16) |
 463                                    ($isbox[$word >> 24 & 0x000000FF] << 24));
 464              ++$i;
 465              $j = ($j + 1) % $Nb;
 466              $k = ($k + 1) % $Nb;
 467              $l = ($l + 1) % $Nb;
 468          }
 469  
 470          return pack('N*', ...$temp);
 471      }
 472  
 473      /**
 474       * Setup the self::ENGINE_INTERNAL $engine
 475       *
 476       * (re)init, if necessary, the internal cipher $engine and flush all $buffers
 477       * Used (only) if $engine == self::ENGINE_INTERNAL
 478       *
 479       * _setup() will be called each time if $changed === true
 480       * typically this happens when using one or more of following public methods:
 481       *
 482       * - setKey()
 483       *
 484       * - setIV()
 485       *
 486       * - disableContinuousBuffer()
 487       *
 488       * - First run of encrypt() / decrypt() with no init-settings
 489       *
 490       * {@internal setup() is always called before en/decryption.}
 491       *
 492       * {@internal Could, but not must, extend by the child Crypt_* class}
 493       *
 494       * @see self::setKey()
 495       * @see self::setIV()
 496       * @see self::disableContinuousBuffer()
 497       */
 498      protected function setup()
 499      {
 500          if (!$this->changed) {
 501              return;
 502          }
 503  
 504          parent::setup();
 505  
 506          if (is_string($this->iv) && strlen($this->iv) != $this->block_size) {
 507              throw new InconsistentSetupException('The IV length (' . strlen($this->iv) . ') does not match the block size (' . $this->block_size . ')');
 508          }
 509      }
 510  
 511      /**
 512       * Setup the key (expansion)
 513       *
 514       * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
 515       */
 516      protected function setupKey()
 517      {
 518          // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
 519          // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
 520          static $rcon;
 521  
 522          if (!isset($rcon)) {
 523              $rcon = [0,
 524                  0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
 525                  0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
 526                  0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
 527                  0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
 528                  0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
 529                  0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
 530              ];
 531              $rcon = array_map('intval', $rcon);
 532          }
 533  
 534          if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_length === $this->kl['key_length'] && $this->block_size === $this->kl['block_size']) {
 535              // already expanded
 536              return;
 537          }
 538          $this->kl = ['key' => $this->key, 'key_length' => $this->key_length, 'block_size' => $this->block_size];
 539  
 540          $this->Nk = $this->key_length >> 2;
 541          // see Rijndael-ammended.pdf#page=44
 542          $this->Nr = max($this->Nk, $this->Nb) + 6;
 543  
 544          // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
 545          //     "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
 546          // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
 547          //     "Table 2: Shift offsets for different block lengths"
 548          switch ($this->Nb) {
 549              case 4:
 550              case 5:
 551              case 6:
 552                  $this->c = [0, 1, 2, 3];
 553                  break;
 554              case 7:
 555                  $this->c = [0, 1, 2, 4];
 556                  break;
 557              case 8:
 558                  $this->c = [0, 1, 3, 4];
 559          }
 560  
 561          $w = array_values(unpack('N*words', $this->key));
 562  
 563          $length = $this->Nb * ($this->Nr + 1);
 564          for ($i = $this->Nk; $i < $length; $i++) {
 565              $temp = $w[$i - 1];
 566              if ($i % $this->Nk == 0) {
 567                  // according to <http://php.net/language.types.integer>, "the size of an integer is platform-dependent".
 568                  // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
 569                  // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
 570                  // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
 571                  $temp = (($temp << 8) & intval(0xFFFFFF00)) | (($temp >> 24) & 0x000000FF); // rotWord
 572                  $temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk];
 573              } elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
 574                  $temp = $this->subWord($temp);
 575              }
 576              $w[$i] = $w[$i - $this->Nk] ^ $temp;
 577          }
 578  
 579          // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
 580          // and generate the inverse key schedule.  more specifically,
 581          // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
 582          // "The key expansion for the Inverse Cipher is defined as follows:
 583          //        1. Apply the Key Expansion.
 584          //        2. Apply InvMixColumn to all Round Keys except the first and the last one."
 585          // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
 586          list($dt0, $dt1, $dt2, $dt3) = $this->getInvTables();
 587          $temp = $this->w = $this->dw = [];
 588          for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
 589              if ($col == $this->Nb) {
 590                  if ($row == 0) {
 591                      $this->dw[0] = $this->w[0];
 592                  } else {
 593                      // subWord + invMixColumn + invSubWord = invMixColumn
 594                      $j = 0;
 595                      while ($j < $this->Nb) {
 596                          $dw = $this->subWord($this->w[$row][$j]);
 597                          $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
 598                                      $dt1[$dw >> 16 & 0x000000FF] ^
 599                                      $dt2[$dw >>  8 & 0x000000FF] ^
 600                                      $dt3[$dw       & 0x000000FF];
 601                          $j++;
 602                      }
 603                      $this->dw[$row] = $temp;
 604                  }
 605  
 606                  $col = 0;
 607                  $row++;
 608              }
 609              $this->w[$row][$col] = $w[$i];
 610          }
 611  
 612          $this->dw[$row] = $this->w[$row];
 613  
 614          // Converting to 1-dim key arrays (both ascending)
 615          $this->dw = array_reverse($this->dw);
 616          $w  = array_pop($this->w);
 617          $dw = array_pop($this->dw);
 618          foreach ($this->w as $r => $wr) {
 619              foreach ($wr as $c => $wc) {
 620                  $w[]  = $wc;
 621                  $dw[] = $this->dw[$r][$c];
 622              }
 623          }
 624          $this->w  = $w;
 625          $this->dw = $dw;
 626      }
 627  
 628      /**
 629       * Performs S-Box substitutions
 630       *
 631       * @return array
 632       * @param int $word
 633       */
 634      private function subWord($word)
 635      {
 636          static $sbox;
 637          if (empty($sbox)) {
 638              list(, , , , $sbox) = self::getTables();
 639          }
 640  
 641          return  $sbox[$word       & 0x000000FF]        |
 642                 ($sbox[$word >>  8 & 0x000000FF] <<  8) |
 643                 ($sbox[$word >> 16 & 0x000000FF] << 16) |
 644                 ($sbox[$word >> 24 & 0x000000FF] << 24);
 645      }
 646  
 647      /**
 648       * Provides the mixColumns and sboxes tables
 649       *
 650       * @see self::encryptBlock()
 651       * @see self::setupInlineCrypt()
 652       * @see self::subWord()
 653       * @return array &$tables
 654       */
 655      protected function &getTables()
 656      {
 657          static $tables;
 658          if (empty($tables)) {
 659              // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
 660              // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
 661              // those are the names we'll use.
 662              $t3 = array_map('intval', [
 663                  // with array_map('intval', ...) we ensure we have only int's and not
 664                  // some slower floats converted by php automatically on high values
 665                  0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
 666                  0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
 667                  0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
 668                  0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
 669                  0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
 670                  0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
 671                  0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
 672                  0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
 673                  0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
 674                  0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
 675                  0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
 676                  0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
 677                  0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
 678                  0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
 679                  0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
 680                  0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
 681                  0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
 682                  0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
 683                  0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
 684                  0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
 685                  0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
 686                  0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
 687                  0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
 688                  0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
 689                  0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
 690                  0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
 691                  0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
 692                  0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
 693                  0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
 694                  0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
 695                  0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
 696                  0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
 697              ]);
 698  
 699              foreach ($t3 as $t3i) {
 700                  $t0[] = (($t3i << 24) & intval(0xFF000000)) | (($t3i >>  8) & 0x00FFFFFF);
 701                  $t1[] = (($t3i << 16) & intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
 702                  $t2[] = (($t3i <<  8) & intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
 703              }
 704  
 705              $tables = [
 706                  // The Precomputed mixColumns tables t0 - t3
 707                  $t0,
 708                  $t1,
 709                  $t2,
 710                  $t3,
 711                  // The SubByte S-Box
 712                  [
 713                      0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
 714                      0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
 715                      0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
 716                      0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
 717                      0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
 718                      0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
 719                      0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
 720                      0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
 721                      0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
 722                      0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
 723                      0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
 724                      0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
 725                      0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
 726                      0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
 727                      0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
 728                      0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
 729                  ]
 730              ];
 731          }
 732          return $tables;
 733      }
 734  
 735      /**
 736       * Provides the inverse mixColumns and inverse sboxes tables
 737       *
 738       * @see self::decryptBlock()
 739       * @see self::setupInlineCrypt()
 740       * @see self::setupKey()
 741       * @return array &$tables
 742       */
 743      protected function &getInvTables()
 744      {
 745          static $tables;
 746          if (empty($tables)) {
 747              $dt3 = array_map('intval', [
 748                  0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
 749                  0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
 750                  0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
 751                  0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
 752                  0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
 753                  0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
 754                  0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
 755                  0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
 756                  0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
 757                  0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
 758                  0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
 759                  0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
 760                  0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
 761                  0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
 762                  0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
 763                  0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
 764                  0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
 765                  0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
 766                  0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
 767                  0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
 768                  0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
 769                  0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
 770                  0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
 771                  0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
 772                  0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
 773                  0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
 774                  0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
 775                  0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
 776                  0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
 777                  0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
 778                  0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
 779                  0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
 780              ]);
 781  
 782              foreach ($dt3 as $dt3i) {
 783                  $dt0[] = (($dt3i << 24) & intval(0xFF000000)) | (($dt3i >>  8) & 0x00FFFFFF);
 784                  $dt1[] = (($dt3i << 16) & intval(0xFFFF0000)) | (($dt3i >> 16) & 0x0000FFFF);
 785                  $dt2[] = (($dt3i <<  8) & intval(0xFFFFFF00)) | (($dt3i >> 24) & 0x000000FF);
 786              };
 787  
 788              $tables = [
 789                  // The Precomputed inverse mixColumns tables dt0 - dt3
 790                  $dt0,
 791                  $dt1,
 792                  $dt2,
 793                  $dt3,
 794                  // The inverse SubByte S-Box
 795                  [
 796                      0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
 797                      0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
 798                      0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
 799                      0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
 800                      0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
 801                      0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
 802                      0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
 803                      0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
 804                      0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
 805                      0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
 806                      0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
 807                      0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
 808                      0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
 809                      0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
 810                      0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
 811                      0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
 812                  ]
 813              ];
 814          }
 815          return $tables;
 816      }
 817  
 818      /**
 819       * Setup the performance-optimized function for de/encrypt()
 820       *
 821       * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
 822       */
 823      protected function setupInlineCrypt()
 824      {
 825          $w  = $this->w;
 826          $dw = $this->dw;
 827          $init_encrypt = '';
 828          $init_decrypt = '';
 829  
 830          $Nr = $this->Nr;
 831          $Nb = $this->Nb;
 832          $c  = $this->c;
 833  
 834          // Generating encrypt code:
 835          $init_encrypt .= '
 836              if (empty($tables)) {
 837                  $tables = &$this->getTables();
 838              }
 839              $t0   = $tables[0];
 840              $t1   = $tables[1];
 841              $t2   = $tables[2];
 842              $t3   = $tables[3];
 843              $sbox = $tables[4];
 844          ';
 845  
 846          $s  = 'e';
 847          $e  = 's';
 848          $wc = $Nb - 1;
 849  
 850          // Preround: addRoundKey
 851          $encrypt_block = '$in = unpack("N*", $in);' . "\n";
 852          for ($i = 0; $i < $Nb; ++$i) {
 853              $encrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $w[++$wc] . ";\n";
 854          }
 855  
 856          // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
 857          for ($round = 1; $round < $Nr; ++$round) {
 858              list($s, $e) = [$e, $s];
 859              for ($i = 0; $i < $Nb; ++$i) {
 860                  $encrypt_block .=
 861                      '$' . $e . $i . ' =
 862                      $t0[($' . $s . $i                  . ' >> 24) & 0xff] ^
 863                      $t1[($' . $s . (($i + $c[1]) % $Nb) . ' >> 16) & 0xff] ^
 864                      $t2[($' . $s . (($i + $c[2]) % $Nb) . ' >>  8) & 0xff] ^
 865                      $t3[ $' . $s . (($i + $c[3]) % $Nb) . '        & 0xff] ^
 866                      ' . $w[++$wc] . ";\n";
 867              }
 868          }
 869  
 870          // Finalround: subWord + shiftRows + addRoundKey
 871          for ($i = 0; $i < $Nb; ++$i) {
 872              $encrypt_block .=
 873                  '$' . $e . $i . ' =
 874                   $sbox[ $' . $e . $i . '        & 0xff]        |
 875                  ($sbox[($' . $e . $i . ' >>  8) & 0xff] <<  8) |
 876                  ($sbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
 877                  ($sbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
 878          }
 879          $encrypt_block .= '$in = pack("N*"' . "\n";
 880          for ($i = 0; $i < $Nb; ++$i) {
 881              $encrypt_block .= ',
 882                  ($' . $e . $i                  . ' & ' . ((int)0xFF000000) . ') ^
 883                  ($' . $e . (($i + $c[1]) % $Nb) . ' &         0x00FF0000   ) ^
 884                  ($' . $e . (($i + $c[2]) % $Nb) . ' &         0x0000FF00   ) ^
 885                  ($' . $e . (($i + $c[3]) % $Nb) . ' &         0x000000FF   ) ^
 886                  ' . $w[$i] . "\n";
 887          }
 888          $encrypt_block .= ');';
 889  
 890          // Generating decrypt code:
 891          $init_decrypt .= '
 892              if (empty($invtables)) {
 893                  $invtables = &$this->getInvTables();
 894              }
 895              $dt0   = $invtables[0];
 896              $dt1   = $invtables[1];
 897              $dt2   = $invtables[2];
 898              $dt3   = $invtables[3];
 899              $isbox = $invtables[4];
 900          ';
 901  
 902          $s  = 'e';
 903          $e  = 's';
 904          $wc = $Nb - 1;
 905  
 906          // Preround: addRoundKey
 907          $decrypt_block = '$in = unpack("N*", $in);' . "\n";
 908          for ($i = 0; $i < $Nb; ++$i) {
 909              $decrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $dw[++$wc] . ';' . "\n";
 910          }
 911  
 912          // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
 913          for ($round = 1; $round < $Nr; ++$round) {
 914              list($s, $e) = [$e, $s];
 915              for ($i = 0; $i < $Nb; ++$i) {
 916                  $decrypt_block .=
 917                      '$' . $e . $i . ' =
 918                      $dt0[($' . $s . $i                        . ' >> 24) & 0xff] ^
 919                      $dt1[($' . $s . (($Nb + $i - $c[1]) % $Nb) . ' >> 16) & 0xff] ^
 920                      $dt2[($' . $s . (($Nb + $i - $c[2]) % $Nb) . ' >>  8) & 0xff] ^
 921                      $dt3[ $' . $s . (($Nb + $i - $c[3]) % $Nb) . '        & 0xff] ^
 922                      ' . $dw[++$wc] . ";\n";
 923              }
 924          }
 925  
 926          // Finalround: subWord + shiftRows + addRoundKey
 927          for ($i = 0; $i < $Nb; ++$i) {
 928              $decrypt_block .=
 929                  '$' . $e . $i . ' =
 930                   $isbox[ $' . $e . $i . '        & 0xff]        |
 931                  ($isbox[($' . $e . $i . ' >>  8) & 0xff] <<  8) |
 932                  ($isbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
 933                  ($isbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
 934          }
 935          $decrypt_block .= '$in = pack("N*"' . "\n";
 936          for ($i = 0; $i < $Nb; ++$i) {
 937              $decrypt_block .= ',
 938                  ($' . $e . $i .                        ' & ' . ((int)0xFF000000) . ') ^
 939                  ($' . $e . (($Nb + $i - $c[1]) % $Nb) . ' &         0x00FF0000   ) ^
 940                  ($' . $e . (($Nb + $i - $c[2]) % $Nb) . ' &         0x0000FF00   ) ^
 941                  ($' . $e . (($Nb + $i - $c[3]) % $Nb) . ' &         0x000000FF   ) ^
 942                  ' . $dw[$i] . "\n";
 943          }
 944          $decrypt_block .= ');';
 945  
 946          $this->inline_crypt = $this->createInlineCryptFunction(
 947              [
 948                 'init_crypt'    => 'static $tables; static $invtables;',
 949                 'init_encrypt'  => $init_encrypt,
 950                 'init_decrypt'  => $init_decrypt,
 951                 'encrypt_block' => $encrypt_block,
 952                 'decrypt_block' => $decrypt_block
 953              ]
 954          );
 955      }
 956  
 957      /**
 958       * Encrypts a message.
 959       *
 960       * @see self::decrypt()
 961       * @see parent::encrypt()
 962       * @param string $plaintext
 963       * @return string
 964       */
 965      public function encrypt($plaintext)
 966      {
 967          $this->setup();
 968  
 969          switch ($this->engine) {
 970              case self::ENGINE_LIBSODIUM:
 971                  $this->newtag = sodium_crypto_aead_aes256gcm_encrypt($plaintext, $this->aad, $this->nonce, $this->key);
 972                  return Strings::shift($this->newtag, strlen($plaintext));
 973              case self::ENGINE_OPENSSL_GCM:
 974                  return openssl_encrypt(
 975                      $plaintext,
 976                      'aes-' . $this->getKeyLength() . '-gcm',
 977                      $this->key,
 978                      OPENSSL_RAW_DATA,
 979                      $this->nonce,
 980                      $this->newtag,
 981                      $this->aad
 982                  );
 983          }
 984  
 985          return parent::encrypt($plaintext);
 986      }
 987  
 988      /**
 989       * Decrypts a message.
 990       *
 991       * @see self::encrypt()
 992       * @see parent::decrypt()
 993       * @param string $ciphertext
 994       * @return string
 995       */
 996      public function decrypt($ciphertext)
 997      {
 998          $this->setup();
 999  
1000          switch ($this->engine) {
1001              case self::ENGINE_LIBSODIUM:
1002                  if ($this->oldtag === false) {
1003                      throw new InsufficientSetupException('Authentication Tag has not been set');
1004                  }
1005                  if (strlen($this->oldtag) != 16) {
1006                      break;
1007                  }
1008                  $plaintext = sodium_crypto_aead_aes256gcm_decrypt($ciphertext . $this->oldtag, $this->aad, $this->nonce, $this->key);
1009                  if ($plaintext === false) {
1010                      $this->oldtag = false;
1011                      throw new BadDecryptionException('Error decrypting ciphertext with libsodium');
1012                  }
1013                  return $plaintext;
1014              case self::ENGINE_OPENSSL_GCM:
1015                  if ($this->oldtag === false) {
1016                      throw new InsufficientSetupException('Authentication Tag has not been set');
1017                  }
1018                  $plaintext = openssl_decrypt(
1019                      $ciphertext,
1020                      'aes-' . $this->getKeyLength() . '-gcm',
1021                      $this->key,
1022                      OPENSSL_RAW_DATA,
1023                      $this->nonce,
1024                      $this->oldtag,
1025                      $this->aad
1026                  );
1027                  if ($plaintext === false) {
1028                      $this->oldtag = false;
1029                      throw new BadDecryptionException('Error decrypting ciphertext with OpenSSL');
1030                  }
1031                  return $plaintext;
1032          }
1033  
1034          return parent::decrypt($ciphertext);
1035      }
1036  }