diff -r 48c4eec2b7e6 -r 8c2e4d02f4ef wp/wp-includes/sodium_compat/src/Core/AES.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wp/wp-includes/sodium_compat/src/Core/AES.php Fri Sep 05 18:52:52 2025 +0200 @@ -0,0 +1,518 @@ +orthogonalize(); + self::sbox($q); + $q->orthogonalize(); + return $q[0] & self::U32_MAX; + } + + /** + * Calculate the key schedule from a given random key + * + * @param string $key + * @return ParagonIE_Sodium_Core_AES_KeySchedule + * @throws SodiumException + */ + public static function keySchedule($key) + { + $key_len = self::strlen($key); + switch ($key_len) { + case 16: + $num_rounds = 10; + break; + case 24: + $num_rounds = 12; + break; + case 32: + $num_rounds = 14; + break; + default: + throw new SodiumException('Invalid key length: ' . $key_len); + } + $skey = array(); + $comp_skey = array(); + $nk = $key_len >> 2; + $nkf = ($num_rounds + 1) << 2; + $tmp = 0; + + for ($i = 0; $i < $nk; ++$i) { + $tmp = self::load_4(self::substr($key, $i << 2, 4)); + $skey[($i << 1)] = $tmp; + $skey[($i << 1) + 1] = $tmp; + } + + for ($i = $nk, $j = 0, $k = 0; $i < $nkf; ++$i) { + if ($j === 0) { + $tmp = (($tmp & 0xff) << 24) | ($tmp >> 8); + $tmp = (self::subWord($tmp) ^ self::$Rcon[$k]) & self::U32_MAX; + } elseif ($nk > 6 && $j === 4) { + $tmp = self::subWord($tmp); + } + $tmp ^= $skey[($i - $nk) << 1]; + $skey[($i << 1)] = $tmp & self::U32_MAX; + $skey[($i << 1) + 1] = $tmp & self::U32_MAX; + if (++$j === $nk) { + /** @psalm-suppress LoopInvalidation */ + $j = 0; + ++$k; + } + } + for ($i = 0; $i < $nkf; $i += 4) { + $q = ParagonIE_Sodium_Core_AES_Block::fromArray( + array_slice($skey, $i << 1, 8) + ); + $q->orthogonalize(); + // We have to overwrite $skey since we're not using C pointers like BearSSL did + for ($j = 0; $j < 8; ++$j) { + $skey[($i << 1) + $j] = $q[$j]; + } + } + for ($i = 0, $j = 0; $i < $nkf; ++$i, $j += 2) { + $comp_skey[$i] = ($skey[$j] & 0x55555555) + | ($skey[$j + 1] & 0xAAAAAAAA); + } + return new ParagonIE_Sodium_Core_AES_KeySchedule($comp_skey, $num_rounds); + } + + /** + * Mutates $q + * + * @param ParagonIE_Sodium_Core_AES_KeySchedule $skey + * @param ParagonIE_Sodium_Core_AES_Block $q + * @param int $offset + * @return void + */ + public static function addRoundKey( + ParagonIE_Sodium_Core_AES_Block $q, + ParagonIE_Sodium_Core_AES_KeySchedule $skey, + $offset = 0 + ) { + $block = $skey->getRoundKey($offset); + for ($j = 0; $j < 8; ++$j) { + $q[$j] = ($q[$j] ^ $block[$j]) & ParagonIE_Sodium_Core_Util::U32_MAX; + } + } + + /** + * This mainly exists for testing, as we need the round key features for AEGIS. + * + * @param string $message + * @param string $key + * @return string + * @throws SodiumException + */ + public static function decryptBlockECB($message, $key) + { + if (self::strlen($message) !== 16) { + throw new SodiumException('decryptBlockECB() expects a 16 byte message'); + } + $skey = self::keySchedule($key)->expand(); + $q = ParagonIE_Sodium_Core_AES_Block::init(); + $q[0] = self::load_4(self::substr($message, 0, 4)); + $q[2] = self::load_4(self::substr($message, 4, 4)); + $q[4] = self::load_4(self::substr($message, 8, 4)); + $q[6] = self::load_4(self::substr($message, 12, 4)); + + $q->orthogonalize(); + self::bitsliceDecryptBlock($skey, $q); + $q->orthogonalize(); + + return self::store32_le($q[0]) . + self::store32_le($q[2]) . + self::store32_le($q[4]) . + self::store32_le($q[6]); + } + + /** + * This mainly exists for testing, as we need the round key features for AEGIS. + * + * @param string $message + * @param string $key + * @return string + * @throws SodiumException + */ + public static function encryptBlockECB($message, $key) + { + if (self::strlen($message) !== 16) { + throw new SodiumException('encryptBlockECB() expects a 16 byte message'); + } + $comp_skey = self::keySchedule($key); + $skey = $comp_skey->expand(); + $q = ParagonIE_Sodium_Core_AES_Block::init(); + $q[0] = self::load_4(self::substr($message, 0, 4)); + $q[2] = self::load_4(self::substr($message, 4, 4)); + $q[4] = self::load_4(self::substr($message, 8, 4)); + $q[6] = self::load_4(self::substr($message, 12, 4)); + + $q->orthogonalize(); + self::bitsliceEncryptBlock($skey, $q); + $q->orthogonalize(); + + return self::store32_le($q[0]) . + self::store32_le($q[2]) . + self::store32_le($q[4]) . + self::store32_le($q[6]); + } + + /** + * Mutates $q + * + * @param ParagonIE_Sodium_Core_AES_Expanded $skey + * @param ParagonIE_Sodium_Core_AES_Block $q + * @return void + */ + public static function bitsliceEncryptBlock( + ParagonIE_Sodium_Core_AES_Expanded $skey, + ParagonIE_Sodium_Core_AES_Block $q + ) { + self::addRoundKey($q, $skey); + for ($u = 1; $u < $skey->getNumRounds(); ++$u) { + self::sbox($q); + $q->shiftRows(); + $q->mixColumns(); + self::addRoundKey($q, $skey, ($u << 3)); + } + self::sbox($q); + $q->shiftRows(); + self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3)); + } + + /** + * @param string $x + * @param string $y + * @return string + */ + public static function aesRound($x, $y) + { + $q = ParagonIE_Sodium_Core_AES_Block::init(); + $q[0] = self::load_4(self::substr($x, 0, 4)); + $q[2] = self::load_4(self::substr($x, 4, 4)); + $q[4] = self::load_4(self::substr($x, 8, 4)); + $q[6] = self::load_4(self::substr($x, 12, 4)); + + $rk = ParagonIE_Sodium_Core_AES_Block::init(); + $rk[0] = $rk[1] = self::load_4(self::substr($y, 0, 4)); + $rk[2] = $rk[3] = self::load_4(self::substr($y, 4, 4)); + $rk[4] = $rk[5] = self::load_4(self::substr($y, 8, 4)); + $rk[6] = $rk[7] = self::load_4(self::substr($y, 12, 4)); + + $q->orthogonalize(); + self::sbox($q); + $q->shiftRows(); + $q->mixColumns(); + $q->orthogonalize(); + // add round key without key schedule: + for ($i = 0; $i < 8; ++$i) { + $q[$i] ^= $rk[$i]; + } + return self::store32_le($q[0]) . + self::store32_le($q[2]) . + self::store32_le($q[4]) . + self::store32_le($q[6]); + } + + /** + * Process two AES blocks in one shot. + * + * @param string $b0 First AES block + * @param string $rk0 First round key + * @param string $b1 Second AES block + * @param string $rk1 Second round key + * @return string[] + */ + public static function doubleRound($b0, $rk0, $b1, $rk1) + { + $q = ParagonIE_Sodium_Core_AES_Block::init(); + // First block + $q[0] = self::load_4(self::substr($b0, 0, 4)); + $q[2] = self::load_4(self::substr($b0, 4, 4)); + $q[4] = self::load_4(self::substr($b0, 8, 4)); + $q[6] = self::load_4(self::substr($b0, 12, 4)); + // Second block + $q[1] = self::load_4(self::substr($b1, 0, 4)); + $q[3] = self::load_4(self::substr($b1, 4, 4)); + $q[5] = self::load_4(self::substr($b1, 8, 4)); + $q[7] = self::load_4(self::substr($b1, 12, 4));; + + $rk = ParagonIE_Sodium_Core_AES_Block::init(); + // First round key + $rk[0] = self::load_4(self::substr($rk0, 0, 4)); + $rk[2] = self::load_4(self::substr($rk0, 4, 4)); + $rk[4] = self::load_4(self::substr($rk0, 8, 4)); + $rk[6] = self::load_4(self::substr($rk0, 12, 4)); + // Second round key + $rk[1] = self::load_4(self::substr($rk1, 0, 4)); + $rk[3] = self::load_4(self::substr($rk1, 4, 4)); + $rk[5] = self::load_4(self::substr($rk1, 8, 4)); + $rk[7] = self::load_4(self::substr($rk1, 12, 4)); + + $q->orthogonalize(); + self::sbox($q); + $q->shiftRows(); + $q->mixColumns(); + $q->orthogonalize(); + // add round key without key schedule: + for ($i = 0; $i < 8; ++$i) { + $q[$i] ^= $rk[$i]; + } + return array( + self::store32_le($q[0]) . self::store32_le($q[2]) . self::store32_le($q[4]) . self::store32_le($q[6]), + self::store32_le($q[1]) . self::store32_le($q[3]) . self::store32_le($q[5]) . self::store32_le($q[7]), + ); + } + + /** + * @param ParagonIE_Sodium_Core_AES_Expanded $skey + * @param ParagonIE_Sodium_Core_AES_Block $q + * @return void + */ + public static function bitsliceDecryptBlock( + ParagonIE_Sodium_Core_AES_Expanded $skey, + ParagonIE_Sodium_Core_AES_Block $q + ) { + self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3)); + for ($u = $skey->getNumRounds() - 1; $u > 0; --$u) { + $q->inverseShiftRows(); + self::invSbox($q); + self::addRoundKey($q, $skey, ($u << 3)); + $q->inverseMixColumns(); + } + $q->inverseShiftRows(); + self::invSbox($q); + self::addRoundKey($q, $skey, ($u << 3)); + } +}