wp/wp-includes/sodium_compat/src/Core/AES.php
changeset 22 8c2e4d02f4ef
equal deleted inserted replaced
21:48c4eec2b7e6 22:8c2e4d02f4ef
       
     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 }