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
53namespace phpseclib3\Crypt;
54
55use phpseclib3\Common\Functions\Strings;
56use phpseclib3\Crypt\Common\BlockCipher;
57use phpseclib3\Exception\BadDecryptionException;
58use phpseclib3\Exception\BadModeException;
59use phpseclib3\Exception\InconsistentSetupException;
60use phpseclib3\Exception\InsufficientSetupException;
61
62/**
63 * Pure-PHP implementation of Rijndael.
64 *
65 * @author  Jim Wigginton <terrafrost@php.net>
66 */
67class 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}
1037