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