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