[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sat Sep 7 08:20:01 2024 | Cross-referenced by PHPXref |