xref: /dokuwiki/vendor/phpseclib/phpseclib/phpseclib/Crypt/Rijndael.php (revision a896ec97b4d9a77a7ab6956f96aaa0e7987f57d1)
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 Common\SymmetricKey::cipher_name_mcrypt
78     * @see 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] & (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216)) ^
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] & (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216)) |
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([self::class, 'safe_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 = PHP_INT_SIZE == 8 ? // rotWord
572                    (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF) :
573                    ($temp << 8) | (($temp >> 24) & 0x000000FF);
574                $temp = $this->subWord($temp) ^ $rcon[$i / $this->Nk];
575            } elseif ($this->Nk > 6 && $i % $this->Nk == 4) {
576                $temp = $this->subWord($temp);
577            }
578            $w[$i] = $w[$i - $this->Nk] ^ $temp;
579        }
580
581        // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
582        // and generate the inverse key schedule.  more specifically,
583        // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=23> (section 5.3.3),
584        // "The key expansion for the Inverse Cipher is defined as follows:
585        //        1. Apply the Key Expansion.
586        //        2. Apply InvMixColumn to all Round Keys except the first and the last one."
587        // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
588        list($dt0, $dt1, $dt2, $dt3) = $this->getInvTables();
589        $temp = $this->w = $this->dw = [];
590        for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
591            if ($col == $this->Nb) {
592                if ($row == 0) {
593                    $this->dw[0] = $this->w[0];
594                } else {
595                    // subWord + invMixColumn + invSubWord = invMixColumn
596                    $j = 0;
597                    while ($j < $this->Nb) {
598                        $dw = $this->subWord($this->w[$row][$j]);
599                        $temp[$j] = $dt0[$dw >> 24 & 0x000000FF] ^
600                                    $dt1[$dw >> 16 & 0x000000FF] ^
601                                    $dt2[$dw >>  8 & 0x000000FF] ^
602                                    $dt3[$dw       & 0x000000FF];
603                        $j++;
604                    }
605                    $this->dw[$row] = $temp;
606                }
607
608                $col = 0;
609                $row++;
610            }
611            $this->w[$row][$col] = $w[$i];
612        }
613
614        $this->dw[$row] = $this->w[$row];
615
616        // Converting to 1-dim key arrays (both ascending)
617        $this->dw = array_reverse($this->dw);
618        $w  = array_pop($this->w);
619        $dw = array_pop($this->dw);
620        foreach ($this->w as $r => $wr) {
621            foreach ($wr as $c => $wc) {
622                $w[]  = $wc;
623                $dw[] = $this->dw[$r][$c];
624            }
625        }
626        $this->w  = $w;
627        $this->dw = $dw;
628    }
629
630    /**
631     * Performs S-Box substitutions
632     *
633     * @return array
634     * @param int $word
635     */
636    private function subWord($word)
637    {
638        static $sbox;
639        if (empty($sbox)) {
640            list(, , , , $sbox) = self::getTables();
641        }
642
643        return  $sbox[$word       & 0x000000FF]        |
644               ($sbox[$word >>  8 & 0x000000FF] <<  8) |
645               ($sbox[$word >> 16 & 0x000000FF] << 16) |
646               ($sbox[$word >> 24 & 0x000000FF] << 24);
647    }
648
649    /**
650     * Provides the mixColumns and sboxes tables
651     *
652     * @see self::encryptBlock()
653     * @see self::setupInlineCrypt()
654     * @see self::subWord()
655     * @return array &$tables
656     */
657    protected function &getTables()
658    {
659        static $tables;
660        if (empty($tables)) {
661            // according to <http://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf#page=19> (section 5.2.1),
662            // precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
663            // those are the names we'll use.
664            $t3 = array_map([self::class, 'safe_intval'], [
665                // with array_map('intval', ...) we ensure we have only int's and not
666                // some slower floats converted by php automatically on high values
667                0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
668                0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
669                0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
670                0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
671                0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
672                0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
673                0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
674                0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
675                0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
676                0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
677                0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
678                0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
679                0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
680                0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
681                0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
682                0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
683                0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
684                0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
685                0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
686                0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
687                0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
688                0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
689                0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
690                0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
691                0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
692                0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
693                0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
694                0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
695                0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
696                0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
697                0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
698                0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
699            ]);
700
701            foreach ($t3 as $t3i) {
702                $t0[] = (($t3i << 24) & self::safe_intval(0xFF000000)) | (($t3i >>  8) & 0x00FFFFFF);
703                $t1[] = (($t3i << 16) & self::safe_intval(0xFFFF0000)) | (($t3i >> 16) & 0x0000FFFF);
704                $t2[] = (($t3i <<  8) & self::safe_intval(0xFFFFFF00)) | (($t3i >> 24) & 0x000000FF);
705            }
706
707            $tables = [
708                // The Precomputed mixColumns tables t0 - t3
709                $t0,
710                $t1,
711                $t2,
712                $t3,
713                // The SubByte S-Box
714                [
715                    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
716                    0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
717                    0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
718                    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
719                    0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
720                    0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
721                    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
722                    0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
723                    0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
724                    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
725                    0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
726                    0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
727                    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
728                    0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
729                    0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
730                    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
731                ]
732            ];
733        }
734        return $tables;
735    }
736
737    /**
738     * Provides the inverse mixColumns and inverse sboxes tables
739     *
740     * @see self::decryptBlock()
741     * @see self::setupInlineCrypt()
742     * @see self::setupKey()
743     * @return array &$tables
744     */
745    protected function &getInvTables()
746    {
747        static $tables;
748        if (empty($tables)) {
749            $dt3 = array_map([self::class, 'safe_intval'], [
750                0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
751                0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
752                0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
753                0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
754                0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
755                0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
756                0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
757                0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
758                0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
759                0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
760                0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
761                0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
762                0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
763                0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
764                0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
765                0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
766                0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
767                0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
768                0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
769                0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
770                0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
771                0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
772                0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
773                0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
774                0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
775                0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
776                0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
777                0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
778                0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
779                0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
780                0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
781                0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
782            ]);
783
784            if (PHP_INT_SIZE === 8) {
785                foreach ($dt3 as $dt3i) {
786                    $dt0[] = (($dt3i << 24) & 0xFF000000) | (($dt3i >>  8) & 0x00FFFFFF);
787                    $dt1[] = (($dt3i << 16) & 0xFFFF0000) | (($dt3i >> 16) & 0x0000FFFF);
788                    $dt2[] = (($dt3i <<  8) & 0xFFFFFF00) | (($dt3i >> 24) & 0x000000FF);
789                };
790            } else {
791                foreach ($dt3 as $dt3i) {
792                    $dt0[] = ($dt3i << 24) | (($dt3i >>  8) & 0x00FFFFFF);
793                    $dt1[] = ($dt3i << 16) | (($dt3i >> 16) & 0x0000FFFF);
794                    $dt2[] = ($dt3i <<  8) | (($dt3i >> 24) & 0x000000FF);
795                };
796            }
797
798            $tables = [
799                // The Precomputed inverse mixColumns tables dt0 - dt3
800                $dt0,
801                $dt1,
802                $dt2,
803                $dt3,
804                // The inverse SubByte S-Box
805                [
806                    0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
807                    0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
808                    0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
809                    0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
810                    0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
811                    0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
812                    0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
813                    0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
814                    0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
815                    0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
816                    0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
817                    0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
818                    0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
819                    0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
820                    0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
821                    0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
822                ]
823            ];
824        }
825        return $tables;
826    }
827
828    /**
829     * Setup the performance-optimized function for de/encrypt()
830     *
831     * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
832     */
833    protected function setupInlineCrypt()
834    {
835        $w  = $this->w;
836        $dw = $this->dw;
837        $init_encrypt = '';
838        $init_decrypt = '';
839
840        $Nr = $this->Nr;
841        $Nb = $this->Nb;
842        $c  = $this->c;
843
844        // Generating encrypt code:
845        $init_encrypt .= '
846            if (empty($tables)) {
847                $tables = &$this->getTables();
848            }
849            $t0   = $tables[0];
850            $t1   = $tables[1];
851            $t2   = $tables[2];
852            $t3   = $tables[3];
853            $sbox = $tables[4];
854        ';
855
856        $s  = 'e';
857        $e  = 's';
858        $wc = $Nb - 1;
859
860        // Preround: addRoundKey
861        $encrypt_block = '$in = unpack("N*", $in);' . "\n";
862        for ($i = 0; $i < $Nb; ++$i) {
863            $encrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $w[++$wc] . ";\n";
864        }
865
866        // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
867        for ($round = 1; $round < $Nr; ++$round) {
868            list($s, $e) = [$e, $s];
869            for ($i = 0; $i < $Nb; ++$i) {
870                $encrypt_block .=
871                    '$' . $e . $i . ' =
872                    $t0[($' . $s . $i                  . ' >> 24) & 0xff] ^
873                    $t1[($' . $s . (($i + $c[1]) % $Nb) . ' >> 16) & 0xff] ^
874                    $t2[($' . $s . (($i + $c[2]) % $Nb) . ' >>  8) & 0xff] ^
875                    $t3[ $' . $s . (($i + $c[3]) % $Nb) . '        & 0xff] ^
876                    ' . $w[++$wc] . ";\n";
877            }
878        }
879
880        // Finalround: subWord + shiftRows + addRoundKey
881        for ($i = 0; $i < $Nb; ++$i) {
882            $encrypt_block .=
883                '$' . $e . $i . ' =
884                 $sbox[ $' . $e . $i . '        & 0xff]        |
885                ($sbox[($' . $e . $i . ' >>  8) & 0xff] <<  8) |
886                ($sbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
887                ($sbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
888        }
889        $encrypt_block .= '$in = pack("N*"' . "\n";
890        for ($i = 0; $i < $Nb; ++$i) {
891            $encrypt_block .= ',
892                ($' . $e . $i                   . ' & ' . (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216) . ') ^
893                ($' . $e . (($i + $c[1]) % $Nb) . ' &         0x00FF0000   ) ^
894                ($' . $e . (($i + $c[2]) % $Nb) . ' &         0x0000FF00   ) ^
895                ($' . $e . (($i + $c[3]) % $Nb) . ' &         0x000000FF   ) ^
896                ' . $w[$i] . "\n";
897        }
898        $encrypt_block .= ');';
899
900        // Generating decrypt code:
901        $init_decrypt .= '
902            if (empty($invtables)) {
903                $invtables = &$this->getInvTables();
904            }
905            $dt0   = $invtables[0];
906            $dt1   = $invtables[1];
907            $dt2   = $invtables[2];
908            $dt3   = $invtables[3];
909            $isbox = $invtables[4];
910        ';
911
912        $s  = 'e';
913        $e  = 's';
914        $wc = $Nb - 1;
915
916        // Preround: addRoundKey
917        $decrypt_block = '$in = unpack("N*", $in);' . "\n";
918        for ($i = 0; $i < $Nb; ++$i) {
919            $decrypt_block .= '$s' . $i . ' = $in[' . ($i + 1) . '] ^ ' . $dw[++$wc] . ';' . "\n";
920        }
921
922        // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
923        for ($round = 1; $round < $Nr; ++$round) {
924            list($s, $e) = [$e, $s];
925            for ($i = 0; $i < $Nb; ++$i) {
926                $decrypt_block .=
927                    '$' . $e . $i . ' =
928                    $dt0[($' . $s . $i                        . ' >> 24) & 0xff] ^
929                    $dt1[($' . $s . (($Nb + $i - $c[1]) % $Nb) . ' >> 16) & 0xff] ^
930                    $dt2[($' . $s . (($Nb + $i - $c[2]) % $Nb) . ' >>  8) & 0xff] ^
931                    $dt3[ $' . $s . (($Nb + $i - $c[3]) % $Nb) . '        & 0xff] ^
932                    ' . $dw[++$wc] . ";\n";
933            }
934        }
935
936        // Finalround: subWord + shiftRows + addRoundKey
937        for ($i = 0; $i < $Nb; ++$i) {
938            $decrypt_block .=
939                '$' . $e . $i . ' =
940                 $isbox[ $' . $e . $i . '        & 0xff]        |
941                ($isbox[($' . $e . $i . ' >>  8) & 0xff] <<  8) |
942                ($isbox[($' . $e . $i . ' >> 16) & 0xff] << 16) |
943                ($isbox[($' . $e . $i . ' >> 24) & 0xff] << 24);' . "\n";
944        }
945        $decrypt_block .= '$in = pack("N*"' . "\n";
946        for ($i = 0; $i < $Nb; ++$i) {
947            $decrypt_block .= ',
948                ($' . $e . $i .                         ' & ' . (PHP_INT_SIZE == 8 ? 0xFF000000 : -16777216) . ') ^
949                ($' . $e . (($Nb + $i - $c[1]) % $Nb) . ' &         0x00FF0000   ) ^
950                ($' . $e . (($Nb + $i - $c[2]) % $Nb) . ' &         0x0000FF00   ) ^
951                ($' . $e . (($Nb + $i - $c[3]) % $Nb) . ' &         0x000000FF   ) ^
952                ' . $dw[$i] . "\n";
953        }
954        $decrypt_block .= ');';
955
956        $this->inline_crypt = $this->createInlineCryptFunction(
957            [
958               'init_crypt'    => 'static $tables; static $invtables;',
959               'init_encrypt'  => $init_encrypt,
960               'init_decrypt'  => $init_decrypt,
961               'encrypt_block' => $encrypt_block,
962               'decrypt_block' => $decrypt_block
963            ]
964        );
965    }
966
967    /**
968     * Encrypts a message.
969     *
970     * @see self::decrypt()
971     * @see parent::encrypt()
972     * @param string $plaintext
973     * @return string
974     */
975    public function encrypt($plaintext)
976    {
977        $this->setup();
978
979        switch ($this->engine) {
980            case self::ENGINE_LIBSODIUM:
981                $this->newtag = sodium_crypto_aead_aes256gcm_encrypt($plaintext, $this->aad, $this->nonce, $this->key);
982                return Strings::shift($this->newtag, strlen($plaintext));
983            case self::ENGINE_OPENSSL_GCM:
984                return openssl_encrypt(
985                    $plaintext,
986                    'aes-' . $this->getKeyLength() . '-gcm',
987                    $this->key,
988                    OPENSSL_RAW_DATA,
989                    $this->nonce,
990                    $this->newtag,
991                    $this->aad
992                );
993        }
994
995        return parent::encrypt($plaintext);
996    }
997
998    /**
999     * Decrypts a message.
1000     *
1001     * @see self::encrypt()
1002     * @see parent::decrypt()
1003     * @param string $ciphertext
1004     * @return string
1005     */
1006    public function decrypt($ciphertext)
1007    {
1008        $this->setup();
1009
1010        switch ($this->engine) {
1011            case self::ENGINE_LIBSODIUM:
1012                if ($this->oldtag === false) {
1013                    throw new InsufficientSetupException('Authentication Tag has not been set');
1014                }
1015                if (strlen($this->oldtag) != 16) {
1016                    break;
1017                }
1018                $plaintext = sodium_crypto_aead_aes256gcm_decrypt($ciphertext . $this->oldtag, $this->aad, $this->nonce, $this->key);
1019                if ($plaintext === false) {
1020                    $this->oldtag = false;
1021                    throw new BadDecryptionException('Error decrypting ciphertext with libsodium');
1022                }
1023                return $plaintext;
1024            case self::ENGINE_OPENSSL_GCM:
1025                if ($this->oldtag === false) {
1026                    throw new InsufficientSetupException('Authentication Tag has not been set');
1027                }
1028                $plaintext = openssl_decrypt(
1029                    $ciphertext,
1030                    'aes-' . $this->getKeyLength() . '-gcm',
1031                    $this->key,
1032                    OPENSSL_RAW_DATA,
1033                    $this->nonce,
1034                    $this->oldtag,
1035                    $this->aad
1036                );
1037                if ($plaintext === false) {
1038                    $this->oldtag = false;
1039                    throw new BadDecryptionException('Error decrypting ciphertext with OpenSSL');
1040                }
1041                return $plaintext;
1042        }
1043
1044        return parent::decrypt($ciphertext);
1045    }
1046}
1047