[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/sodium_compat/src/Core/ -> AES.php (source)

   1  <?php
   2  
   3  if (class_exists('ParagonIE_Sodium_Core_AES', false)) {
   4      return;
   5  }
   6  
   7  /**
   8   * Bitsliced implementation of the AES block cipher.
   9   *
  10   * Based on the implementation provided by BearSSL.
  11   *
  12   * @internal This should only be used by sodium_compat
  13   */
  14  class ParagonIE_Sodium_Core_AES extends ParagonIE_Sodium_Core_Util
  15  {
  16      /**
  17       * @var int[] AES round constants
  18       */
  19      private static $Rcon = array(
  20          0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
  21      );
  22  
  23      /**
  24       * Mutates the values of $q!
  25       *
  26       * @param ParagonIE_Sodium_Core_AES_Block $q
  27       * @return void
  28       */
  29      public static function sbox(ParagonIE_Sodium_Core_AES_Block $q)
  30      {
  31          /**
  32           * @var int $x0
  33           * @var int $x1
  34           * @var int $x2
  35           * @var int $x3
  36           * @var int $x4
  37           * @var int $x5
  38           * @var int $x6
  39           * @var int $x7
  40           */
  41          $x0 = $q[7] & self::U32_MAX;
  42          $x1 = $q[6] & self::U32_MAX;
  43          $x2 = $q[5] & self::U32_MAX;
  44          $x3 = $q[4] & self::U32_MAX;
  45          $x4 = $q[3] & self::U32_MAX;
  46          $x5 = $q[2] & self::U32_MAX;
  47          $x6 = $q[1] & self::U32_MAX;
  48          $x7 = $q[0] & self::U32_MAX;
  49  
  50          $y14 = $x3 ^ $x5;
  51          $y13 = $x0 ^ $x6;
  52          $y9 = $x0 ^ $x3;
  53          $y8 = $x0 ^ $x5;
  54          $t0 = $x1 ^ $x2;
  55          $y1 = $t0 ^ $x7;
  56          $y4 = $y1 ^ $x3;
  57          $y12 = $y13 ^ $y14;
  58          $y2 = $y1 ^ $x0;
  59          $y5 = $y1 ^ $x6;
  60          $y3 = $y5 ^ $y8;
  61          $t1 = $x4 ^ $y12;
  62          $y15 = $t1 ^ $x5;
  63          $y20 = $t1 ^ $x1;
  64          $y6 = $y15 ^ $x7;
  65          $y10 = $y15 ^ $t0;
  66          $y11 = $y20 ^ $y9;
  67          $y7 = $x7 ^ $y11;
  68          $y17 = $y10 ^ $y11;
  69          $y19 = $y10 ^ $y8;
  70          $y16 = $t0 ^ $y11;
  71          $y21 = $y13 ^ $y16;
  72          $y18 = $x0 ^ $y16;
  73  
  74          /*
  75           * Non-linear section.
  76           */
  77          $t2 = $y12 & $y15;
  78          $t3 = $y3 & $y6;
  79          $t4 = $t3 ^ $t2;
  80          $t5 = $y4 & $x7;
  81          $t6 = $t5 ^ $t2;
  82          $t7 = $y13 & $y16;
  83          $t8 = $y5 & $y1;
  84          $t9 = $t8 ^ $t7;
  85          $t10 = $y2 & $y7;
  86          $t11 = $t10 ^ $t7;
  87          $t12 = $y9 & $y11;
  88          $t13 = $y14 & $y17;
  89          $t14 = $t13 ^ $t12;
  90          $t15 = $y8 & $y10;
  91          $t16 = $t15 ^ $t12;
  92          $t17 = $t4 ^ $t14;
  93          $t18 = $t6 ^ $t16;
  94          $t19 = $t9 ^ $t14;
  95          $t20 = $t11 ^ $t16;
  96          $t21 = $t17 ^ $y20;
  97          $t22 = $t18 ^ $y19;
  98          $t23 = $t19 ^ $y21;
  99          $t24 = $t20 ^ $y18;
 100  
 101          $t25 = $t21 ^ $t22;
 102          $t26 = $t21 & $t23;
 103          $t27 = $t24 ^ $t26;
 104          $t28 = $t25 & $t27;
 105          $t29 = $t28 ^ $t22;
 106          $t30 = $t23 ^ $t24;
 107          $t31 = $t22 ^ $t26;
 108          $t32 = $t31 & $t30;
 109          $t33 = $t32 ^ $t24;
 110          $t34 = $t23 ^ $t33;
 111          $t35 = $t27 ^ $t33;
 112          $t36 = $t24 & $t35;
 113          $t37 = $t36 ^ $t34;
 114          $t38 = $t27 ^ $t36;
 115          $t39 = $t29 & $t38;
 116          $t40 = $t25 ^ $t39;
 117  
 118          $t41 = $t40 ^ $t37;
 119          $t42 = $t29 ^ $t33;
 120          $t43 = $t29 ^ $t40;
 121          $t44 = $t33 ^ $t37;
 122          $t45 = $t42 ^ $t41;
 123          $z0 = $t44 & $y15;
 124          $z1 = $t37 & $y6;
 125          $z2 = $t33 & $x7;
 126          $z3 = $t43 & $y16;
 127          $z4 = $t40 & $y1;
 128          $z5 = $t29 & $y7;
 129          $z6 = $t42 & $y11;
 130          $z7 = $t45 & $y17;
 131          $z8 = $t41 & $y10;
 132          $z9 = $t44 & $y12;
 133          $z10 = $t37 & $y3;
 134          $z11 = $t33 & $y4;
 135          $z12 = $t43 & $y13;
 136          $z13 = $t40 & $y5;
 137          $z14 = $t29 & $y2;
 138          $z15 = $t42 & $y9;
 139          $z16 = $t45 & $y14;
 140          $z17 = $t41 & $y8;
 141  
 142          /*
 143           * Bottom linear transformation.
 144           */
 145          $t46 = $z15 ^ $z16;
 146          $t47 = $z10 ^ $z11;
 147          $t48 = $z5 ^ $z13;
 148          $t49 = $z9 ^ $z10;
 149          $t50 = $z2 ^ $z12;
 150          $t51 = $z2 ^ $z5;
 151          $t52 = $z7 ^ $z8;
 152          $t53 = $z0 ^ $z3;
 153          $t54 = $z6 ^ $z7;
 154          $t55 = $z16 ^ $z17;
 155          $t56 = $z12 ^ $t48;
 156          $t57 = $t50 ^ $t53;
 157          $t58 = $z4 ^ $t46;
 158          $t59 = $z3 ^ $t54;
 159          $t60 = $t46 ^ $t57;
 160          $t61 = $z14 ^ $t57;
 161          $t62 = $t52 ^ $t58;
 162          $t63 = $t49 ^ $t58;
 163          $t64 = $z4 ^ $t59;
 164          $t65 = $t61 ^ $t62;
 165          $t66 = $z1 ^ $t63;
 166          $s0 = $t59 ^ $t63;
 167          $s6 = $t56 ^ ~$t62;
 168          $s7 = $t48 ^ ~$t60;
 169          $t67 = $t64 ^ $t65;
 170          $s3 = $t53 ^ $t66;
 171          $s4 = $t51 ^ $t66;
 172          $s5 = $t47 ^ $t65;
 173          $s1 = $t64 ^ ~$s3;
 174          $s2 = $t55 ^ ~$t67;
 175  
 176          $q[7] = $s0 & self::U32_MAX;
 177          $q[6] = $s1 & self::U32_MAX;
 178          $q[5] = $s2 & self::U32_MAX;
 179          $q[4] = $s3 & self::U32_MAX;
 180          $q[3] = $s4 & self::U32_MAX;
 181          $q[2] = $s5 & self::U32_MAX;
 182          $q[1] = $s6 & self::U32_MAX;
 183          $q[0] = $s7 & self::U32_MAX;
 184      }
 185  
 186      /**
 187       * Mutates the values of $q!
 188       *
 189       * @param ParagonIE_Sodium_Core_AES_Block $q
 190       * @return void
 191       */
 192      public static function invSbox(ParagonIE_Sodium_Core_AES_Block $q)
 193      {
 194          self::processInversion($q);
 195          self::sbox($q);
 196          self::processInversion($q);
 197      }
 198  
 199      /**
 200       * This is some boilerplate code needed to invert an S-box. Rather than repeat the code
 201       * twice, I moved it to a protected method.
 202       *
 203       * Mutates $q
 204       *
 205       * @param ParagonIE_Sodium_Core_AES_Block $q
 206       * @return void
 207       */
 208      protected static function processInversion(ParagonIE_Sodium_Core_AES_Block $q)
 209      {
 210          $q0 = (~$q[0]) & self::U32_MAX;
 211          $q1 = (~$q[1]) & self::U32_MAX;
 212          $q2 = $q[2] & self::U32_MAX;
 213          $q3 = $q[3] & self::U32_MAX;
 214          $q4 = $q[4] & self::U32_MAX;
 215          $q5 = (~$q[5])  & self::U32_MAX;
 216          $q6 = (~$q[6])  & self::U32_MAX;
 217          $q7 = $q[7] & self::U32_MAX;
 218          $q[7] = ($q1 ^ $q4 ^ $q6) & self::U32_MAX;
 219          $q[6] = ($q0 ^ $q3 ^ $q5) & self::U32_MAX;
 220          $q[5] = ($q7 ^ $q2 ^ $q4) & self::U32_MAX;
 221          $q[4] = ($q6 ^ $q1 ^ $q3) & self::U32_MAX;
 222          $q[3] = ($q5 ^ $q0 ^ $q2) & self::U32_MAX;
 223          $q[2] = ($q4 ^ $q7 ^ $q1) & self::U32_MAX;
 224          $q[1] = ($q3 ^ $q6 ^ $q0) & self::U32_MAX;
 225          $q[0] = ($q2 ^ $q5 ^ $q7) & self::U32_MAX;
 226      }
 227  
 228      /**
 229       * @param int $x
 230       * @return int
 231       */
 232      public static function subWord($x)
 233      {
 234          $q = ParagonIE_Sodium_Core_AES_Block::fromArray(
 235              array($x, $x, $x, $x, $x, $x, $x, $x)
 236          );
 237          $q->orthogonalize();
 238          self::sbox($q);
 239          $q->orthogonalize();
 240          return $q[0] & self::U32_MAX;
 241      }
 242  
 243      /**
 244       * Calculate the key schedule from a given random key
 245       *
 246       * @param string $key
 247       * @return ParagonIE_Sodium_Core_AES_KeySchedule
 248       * @throws SodiumException
 249       */
 250      public static function keySchedule($key)
 251      {
 252          $key_len = self::strlen($key);
 253          switch ($key_len) {
 254              case 16:
 255                  $num_rounds = 10;
 256                  break;
 257              case 24:
 258                  $num_rounds = 12;
 259                  break;
 260              case 32:
 261                  $num_rounds = 14;
 262                  break;
 263              default:
 264                  throw new SodiumException('Invalid key length: ' . $key_len);
 265          }
 266          $skey = array();
 267          $comp_skey = array();
 268          $nk = $key_len >> 2;
 269          $nkf = ($num_rounds + 1) << 2;
 270          $tmp = 0;
 271  
 272          for ($i = 0; $i < $nk; ++$i) {
 273              $tmp = self::load_4(self::substr($key, $i << 2, 4));
 274              $skey[($i << 1)] = $tmp;
 275              $skey[($i << 1) + 1] = $tmp;
 276          }
 277  
 278          for ($i = $nk, $j = 0, $k = 0; $i < $nkf; ++$i) {
 279              if ($j === 0) {
 280                  $tmp = (($tmp & 0xff) << 24) | ($tmp >> 8);
 281                  $tmp = (self::subWord($tmp) ^ self::$Rcon[$k]) & self::U32_MAX;
 282              } elseif ($nk > 6 && $j === 4) {
 283                  $tmp = self::subWord($tmp);
 284              }
 285              $tmp ^= $skey[($i - $nk) << 1];
 286              $skey[($i << 1)] = $tmp & self::U32_MAX;
 287              $skey[($i << 1) + 1] = $tmp & self::U32_MAX;
 288              if (++$j === $nk) {
 289                  /** @psalm-suppress LoopInvalidation */
 290                  $j = 0;
 291                  ++$k;
 292              }
 293          }
 294          for ($i = 0; $i < $nkf; $i += 4) {
 295              $q = ParagonIE_Sodium_Core_AES_Block::fromArray(
 296                  array_slice($skey, $i << 1, 8)
 297              );
 298              $q->orthogonalize();
 299              // We have to overwrite $skey since we're not using C pointers like BearSSL did
 300              for ($j = 0; $j < 8; ++$j) {
 301                  $skey[($i << 1) + $j] = $q[$j];
 302              }
 303          }
 304          for ($i = 0, $j = 0; $i < $nkf; ++$i, $j += 2) {
 305              $comp_skey[$i] = ($skey[$j] & 0x55555555)
 306                  | ($skey[$j + 1] & 0xAAAAAAAA);
 307          }
 308          return new ParagonIE_Sodium_Core_AES_KeySchedule($comp_skey, $num_rounds);
 309      }
 310  
 311      /**
 312       * Mutates $q
 313       *
 314       * @param ParagonIE_Sodium_Core_AES_KeySchedule $skey
 315       * @param ParagonIE_Sodium_Core_AES_Block $q
 316       * @param int $offset
 317       * @return void
 318       */
 319      public static function addRoundKey(
 320          ParagonIE_Sodium_Core_AES_Block $q,
 321          ParagonIE_Sodium_Core_AES_KeySchedule $skey,
 322          $offset = 0
 323      ) {
 324          $block = $skey->getRoundKey($offset);
 325          for ($j = 0; $j < 8; ++$j) {
 326              $q[$j] = ($q[$j] ^ $block[$j]) & ParagonIE_Sodium_Core_Util::U32_MAX;
 327          }
 328      }
 329  
 330      /**
 331       * This mainly exists for testing, as we need the round key features for AEGIS.
 332       *
 333       * @param string $message
 334       * @param string $key
 335       * @return string
 336       * @throws SodiumException
 337       */
 338      public static function decryptBlockECB($message, $key)
 339      {
 340          if (self::strlen($message) !== 16) {
 341              throw new SodiumException('decryptBlockECB() expects a 16 byte message');
 342          }
 343          $skey = self::keySchedule($key)->expand();
 344          $q = ParagonIE_Sodium_Core_AES_Block::init();
 345          $q[0] = self::load_4(self::substr($message, 0, 4));
 346          $q[2] = self::load_4(self::substr($message, 4, 4));
 347          $q[4] = self::load_4(self::substr($message, 8, 4));
 348          $q[6] = self::load_4(self::substr($message, 12, 4));
 349  
 350          $q->orthogonalize();
 351          self::bitsliceDecryptBlock($skey, $q);
 352          $q->orthogonalize();
 353  
 354          return self::store32_le($q[0]) .
 355              self::store32_le($q[2]) .
 356              self::store32_le($q[4]) .
 357              self::store32_le($q[6]);
 358      }
 359  
 360      /**
 361       * This mainly exists for testing, as we need the round key features for AEGIS.
 362       *
 363       * @param string $message
 364       * @param string $key
 365       * @return string
 366       * @throws SodiumException
 367       */
 368      public static function encryptBlockECB($message, $key)
 369      {
 370          if (self::strlen($message) !== 16) {
 371              throw new SodiumException('encryptBlockECB() expects a 16 byte message');
 372          }
 373          $comp_skey = self::keySchedule($key);
 374          $skey = $comp_skey->expand();
 375          $q = ParagonIE_Sodium_Core_AES_Block::init();
 376          $q[0] = self::load_4(self::substr($message, 0, 4));
 377          $q[2] = self::load_4(self::substr($message, 4, 4));
 378          $q[4] = self::load_4(self::substr($message, 8, 4));
 379          $q[6] = self::load_4(self::substr($message, 12, 4));
 380  
 381          $q->orthogonalize();
 382          self::bitsliceEncryptBlock($skey, $q);
 383          $q->orthogonalize();
 384  
 385          return self::store32_le($q[0]) .
 386              self::store32_le($q[2]) .
 387              self::store32_le($q[4]) .
 388              self::store32_le($q[6]);
 389      }
 390  
 391      /**
 392       * Mutates $q
 393       *
 394       * @param ParagonIE_Sodium_Core_AES_Expanded $skey
 395       * @param ParagonIE_Sodium_Core_AES_Block $q
 396       * @return void
 397       */
 398      public static function bitsliceEncryptBlock(
 399          ParagonIE_Sodium_Core_AES_Expanded $skey,
 400          ParagonIE_Sodium_Core_AES_Block $q
 401      ) {
 402          self::addRoundKey($q, $skey);
 403          for ($u = 1; $u < $skey->getNumRounds(); ++$u) {
 404              self::sbox($q);
 405              $q->shiftRows();
 406              $q->mixColumns();
 407              self::addRoundKey($q, $skey, ($u << 3));
 408          }
 409          self::sbox($q);
 410          $q->shiftRows();
 411          self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3));
 412      }
 413  
 414      /**
 415       * @param string $x
 416       * @param string $y
 417       * @return string
 418       */
 419      public static function aesRound($x, $y)
 420      {
 421          $q = ParagonIE_Sodium_Core_AES_Block::init();
 422          $q[0] = self::load_4(self::substr($x, 0, 4));
 423          $q[2] = self::load_4(self::substr($x, 4, 4));
 424          $q[4] = self::load_4(self::substr($x, 8, 4));
 425          $q[6] = self::load_4(self::substr($x, 12, 4));
 426  
 427          $rk = ParagonIE_Sodium_Core_AES_Block::init();
 428          $rk[0] = $rk[1] = self::load_4(self::substr($y, 0, 4));
 429          $rk[2] = $rk[3] = self::load_4(self::substr($y, 4, 4));
 430          $rk[4] = $rk[5] = self::load_4(self::substr($y, 8, 4));
 431          $rk[6] = $rk[7] = self::load_4(self::substr($y, 12, 4));
 432  
 433          $q->orthogonalize();
 434          self::sbox($q);
 435          $q->shiftRows();
 436          $q->mixColumns();
 437          $q->orthogonalize();
 438          // add round key without key schedule:
 439          for ($i = 0; $i < 8; ++$i) {
 440              $q[$i] ^= $rk[$i];
 441          }
 442          return self::store32_le($q[0]) .
 443              self::store32_le($q[2]) .
 444              self::store32_le($q[4]) .
 445              self::store32_le($q[6]);
 446      }
 447  
 448      /**
 449       * Process two AES blocks in one shot.
 450       *
 451       * @param string $b0  First AES block
 452       * @param string $rk0 First round key
 453       * @param string $b1  Second AES block
 454       * @param string $rk1 Second round key
 455       * @return string[]
 456       */
 457      public static function doubleRound($b0, $rk0, $b1, $rk1)
 458      {
 459          $q = ParagonIE_Sodium_Core_AES_Block::init();
 460          // First block
 461          $q[0] = self::load_4(self::substr($b0, 0, 4));
 462          $q[2] = self::load_4(self::substr($b0, 4, 4));
 463          $q[4] = self::load_4(self::substr($b0, 8, 4));
 464          $q[6] = self::load_4(self::substr($b0, 12, 4));
 465          // Second block
 466          $q[1] = self::load_4(self::substr($b1, 0, 4));
 467          $q[3] = self::load_4(self::substr($b1, 4, 4));
 468          $q[5] = self::load_4(self::substr($b1, 8, 4));
 469          $q[7] = self::load_4(self::substr($b1, 12, 4));;
 470  
 471          $rk = ParagonIE_Sodium_Core_AES_Block::init();
 472          // First round key
 473          $rk[0] = self::load_4(self::substr($rk0, 0, 4));
 474          $rk[2] = self::load_4(self::substr($rk0, 4, 4));
 475          $rk[4] = self::load_4(self::substr($rk0, 8, 4));
 476          $rk[6] = self::load_4(self::substr($rk0, 12, 4));
 477          // Second round key
 478          $rk[1] = self::load_4(self::substr($rk1, 0, 4));
 479          $rk[3] = self::load_4(self::substr($rk1, 4, 4));
 480          $rk[5] = self::load_4(self::substr($rk1, 8, 4));
 481          $rk[7] = self::load_4(self::substr($rk1, 12, 4));
 482  
 483          $q->orthogonalize();
 484          self::sbox($q);
 485          $q->shiftRows();
 486          $q->mixColumns();
 487          $q->orthogonalize();
 488          // add round key without key schedule:
 489          for ($i = 0; $i < 8; ++$i) {
 490              $q[$i] ^= $rk[$i];
 491          }
 492          return array(
 493              self::store32_le($q[0]) . self::store32_le($q[2]) . self::store32_le($q[4]) . self::store32_le($q[6]),
 494              self::store32_le($q[1]) . self::store32_le($q[3]) . self::store32_le($q[5]) . self::store32_le($q[7]),
 495          );
 496      }
 497  
 498      /**
 499       * @param ParagonIE_Sodium_Core_AES_Expanded $skey
 500       * @param ParagonIE_Sodium_Core_AES_Block $q
 501       * @return void
 502       */
 503      public static function bitsliceDecryptBlock(
 504          ParagonIE_Sodium_Core_AES_Expanded $skey,
 505          ParagonIE_Sodium_Core_AES_Block $q
 506      ) {
 507          self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3));
 508          for ($u = $skey->getNumRounds() - 1; $u > 0; --$u) {
 509              $q->inverseShiftRows();
 510              self::invSbox($q);
 511              self::addRoundKey($q, $skey, ($u << 3));
 512              $q->inverseMixColumns();
 513          }
 514          $q->inverseShiftRows();
 515          self::invSbox($q);
 516          self::addRoundKey($q, $skey, ($u << 3));
 517      }
 518  }


Generated : Sat Sep 7 08:20:01 2024 Cross-referenced by PHPXref