diff --git a/src/wp-includes/sodium_compat/src/Core/ChaCha20.php b/src/wp-includes/sodium_compat/src/Core/ChaCha20.php index f116486129576..f3c561e8f7ed8 100644 --- a/src/wp-includes/sodium_compat/src/Core/ChaCha20.php +++ b/src/wp-includes/sodium_compat/src/Core/ChaCha20.php @@ -119,6 +119,9 @@ public static function encryptBytes( $c = ''; for (;;) { + if ($j12 > 0xffffffff || $j13 > 0xffffffff) { + throw new SodiumException('Overflow'); + } if ($bytes < 64) { $message .= str_repeat("\x00", 64 - $bytes); } @@ -258,9 +261,12 @@ public static function encryptBytes( j13 = PLUSONE(j13); } */ - ++$j12; - if ($j12 & 0xf0000000) { - throw new SodiumException('Overflow'); + if ( + ++$j12 > 0xffffffff && + !($ctx instanceof ParagonIE_Sodium_Core_ChaCha20_IetfCtx) + ) { + $j12 = 0; + ++$j13; } /* diff --git a/src/wp-includes/sodium_compat/src/Core/Curve25519.php b/src/wp-includes/sodium_compat/src/Core/Curve25519.php index 7dab1cd0bbd7a..6d6849d423887 100644 --- a/src/wp-includes/sodium_compat/src/Core/Curve25519.php +++ b/src/wp-includes/sodium_compat/src/Core/Curve25519.php @@ -3855,4 +3855,4 @@ public static function fe_normalize(ParagonIE_Sodium_Core_Curve25519_Fe $f) $g->e9 = $e[9]; return $g; } -} \ No newline at end of file +} diff --git a/src/wp-includes/sodium_compat/src/Core/Ed25519.php b/src/wp-includes/sodium_compat/src/Core/Ed25519.php index 5cae0f180c4a7..5328928ef7794 100644 --- a/src/wp-includes/sodium_compat/src/Core/Ed25519.php +++ b/src/wp-includes/sodium_compat/src/Core/Ed25519.php @@ -317,40 +317,47 @@ public static function verify_detached($sig, $message, $pk) if ($d === 0) { throw new SodiumException('All zero public key'); } + if (self::small_order($pk)) { + throw new SodiumException('Public key has small order'); + } /** @var bool The original value of ParagonIE_Sodium_Compat::$fastMult */ $orig = ParagonIE_Sodium_Compat::$fastMult; // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; + try { + /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */ + $A = self::ge_frombytes_negate_vartime($pk); + if (!self::is_on_main_subgroup($A)) { + throw new SodiumException('Public key is not on a member of the main subgroup'); + } - /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */ - $A = self::ge_frombytes_negate_vartime($pk); - if (!self::is_on_main_subgroup($A)) { - throw new SodiumException('Public key is not on a member of the main subgroup'); - } - - /** @var string $hDigest */ - $hDigest = hash( - 'sha512', - self::substr($sig, 0, 32) . - self::substr($pk, 0, 32) . - $message, - true - ); + /** @var string $hDigest */ + $hDigest = hash( + 'sha512', + self::substr($sig, 0, 32) . + self::substr($pk, 0, 32) . + $message, + true + ); - /** @var string $h */ - $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32); + /** @var string $h */ + $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32); - /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ - $R = self::ge_double_scalarmult_vartime( - $h, - $A, - self::substr($sig, 32) - ); + /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ + $R = self::ge_double_scalarmult_vartime( + $h, + $A, + self::substr($sig, 32) + ); - /** @var string $rcheck */ - $rcheck = self::ge_tobytes($R); + /** @var string $rcheck */ + $rcheck = self::ge_tobytes($R); + } catch (Exception $ex) { + ParagonIE_Sodium_Compat::$fastMult = $orig; + throw $ex; + } // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; diff --git a/src/wp-includes/sodium_compat/src/Core/Ristretto255.php b/src/wp-includes/sodium_compat/src/Core/Ristretto255.php index 2727260d2163a..4c43faee3d737 100644 --- a/src/wp-includes/sodium_compat/src/Core/Ristretto255.php +++ b/src/wp-includes/sodium_compat/src/Core/Ristretto255.php @@ -584,6 +584,9 @@ public static function ristretto255_scalar_complement($s) */ public static function ristretto255_scalar_invert($s) { + if (ParagonIE_Sodium_Compat::is_zero($s)) { + throw new SodiumException('Cannot invert a zero scalar'); + } return self::sc25519_invert($s); } diff --git a/src/wp-includes/sodium_compat/src/Core/SecretStream/State.php b/src/wp-includes/sodium_compat/src/Core/SecretStream/State.php index 2412f6549e811..48b7bd835f814 100644 --- a/src/wp-includes/sodium_compat/src/Core/SecretStream/State.php +++ b/src/wp-includes/sodium_compat/src/Core/SecretStream/State.php @@ -96,7 +96,7 @@ public function incrementCounter() */ public function needsRekey() { - return ($this->counter & 0xffff) === 0; + return ($this->counter & 0xffffffff) === 0; } /** diff --git a/src/wp-includes/sodium_compat/src/Core32/ChaCha20.php b/src/wp-includes/sodium_compat/src/Core32/ChaCha20.php index 6f8c0bff9f11a..7002a9cb8455c 100644 --- a/src/wp-includes/sodium_compat/src/Core32/ChaCha20.php +++ b/src/wp-includes/sodium_compat/src/Core32/ChaCha20.php @@ -138,6 +138,9 @@ public static function encryptBytes( $c = ''; for (;;) { + if ($ctx->isExhausted()) { + throw new SodiumException('Overflow'); + } if ($bytes < 64) { $message .= str_repeat("\x00", 64 - $bytes); } @@ -264,7 +267,14 @@ public static function encryptBytes( /** @var ParagonIE_Sodium_Core32_Int32 $j12 */ $j12 = $j12->addInt(1); if ($j12->limbs[0] === 0 && $j12->limbs[1] === 0) { - $j13 = $j13->addInt(1); + if ($ctx instanceof ParagonIE_Sodium_Core32_ChaCha20_IetfCtx) { + $ctx->markExhausted(); + } else { + $j13 = $j13->addInt(1); + if ($j13->limbs[0] === 0 && $j13->limbs[1] === 0) { + $ctx->markExhausted(); + } + } } /* diff --git a/src/wp-includes/sodium_compat/src/Core32/ChaCha20/Ctx.php b/src/wp-includes/sodium_compat/src/Core32/ChaCha20/Ctx.php index 37b058f2503a9..1a70dec036dcd 100644 --- a/src/wp-includes/sodium_compat/src/Core32/ChaCha20/Ctx.php +++ b/src/wp-includes/sodium_compat/src/Core32/ChaCha20/Ctx.php @@ -14,6 +14,21 @@ class ParagonIE_Sodium_Core32_ChaCha20_Ctx extends ParagonIE_Sodium_Core32_Util */ protected $container; + /** @var bool */ + protected $exhausted = false; + + /** @return bool */ + public function isExhausted() + { + return $this->exhausted; + } + + /** @return void */ + public function markExhausted() + { + $this->exhausted = true; + } + /** * ParagonIE_Sodium_Core_ChaCha20_Ctx constructor. * diff --git a/src/wp-includes/sodium_compat/src/Core32/Ed25519.php b/src/wp-includes/sodium_compat/src/Core32/Ed25519.php index 8a0ea8e3e25a7..23d6ef1442ecf 100644 --- a/src/wp-includes/sodium_compat/src/Core32/Ed25519.php +++ b/src/wp-includes/sodium_compat/src/Core32/Ed25519.php @@ -319,34 +319,38 @@ public static function verify_detached($sig, $message, $pk) // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; + try { + /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */ + $A = self::ge_frombytes_negate_vartime($pk); + if (!self::is_on_main_subgroup($A)) { + throw new SodiumException('Public key is not on a member of the main subgroup'); + } - /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */ - $A = self::ge_frombytes_negate_vartime($pk); - if (!self::is_on_main_subgroup($A)) { - throw new SodiumException('Public key is not on a member of the main subgroup'); - } - - /** @var string $hDigest */ - $hDigest = hash( - 'sha512', - self::substr($sig, 0, 32) . - self::substr($pk, 0, 32) . - $message, - true - ); + /** @var string $hDigest */ + $hDigest = hash( + 'sha512', + self::substr($sig, 0, 32) . + self::substr($pk, 0, 32) . + $message, + true + ); - /** @var string $h */ - $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32); + /** @var string $h */ + $h = self::sc_reduce($hDigest) . self::substr($hDigest, 32); - /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ - $R = self::ge_double_scalarmult_vartime( - $h, - $A, - self::substr($sig, 32) - ); + /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ + $R = self::ge_double_scalarmult_vartime( + $h, + $A, + self::substr($sig, 32) + ); - /** @var string $rcheck */ - $rcheck = self::ge_tobytes($R); + /** @var string $rcheck */ + $rcheck = self::ge_tobytes($R); + } catch (Exception $ex) { + ParagonIE_Sodium_Compat::$fastMult = $orig; + throw $ex; + } // Reset ParagonIE_Sodium_Compat::$fastMult to what it was before. ParagonIE_Sodium_Compat::$fastMult = $orig; diff --git a/src/wp-includes/sodium_compat/src/Core32/SecretStream/State.php b/src/wp-includes/sodium_compat/src/Core32/SecretStream/State.php index 6f20486297bf3..d90a2a2ae9b82 100644 --- a/src/wp-includes/sodium_compat/src/Core32/SecretStream/State.php +++ b/src/wp-includes/sodium_compat/src/Core32/SecretStream/State.php @@ -96,7 +96,7 @@ public function incrementCounter() */ public function needsRekey() { - return ($this->counter & 0xffff) === 0; + return ($this->counter & 0xffffffff) === 0; } /** diff --git a/src/wp-includes/sodium_compat/src/File.php b/src/wp-includes/sodium_compat/src/File.php index 658dcd973612a..460d0dcbb3ffc 100644 --- a/src/wp-includes/sodium_compat/src/File.php +++ b/src/wp-includes/sodium_compat/src/File.php @@ -140,16 +140,32 @@ public static function box_open( throw new SodiumException('Could not open input file for reading'); } + /** @var resource $destination */ + $destination = self::openOutputFile($outputFile); + if (!is_resource($destination)) { + fclose($ifp); + throw new SodiumException('Could not open output file for writing'); + } /** @var resource $ofp */ - $ofp = @fopen($outputFile, 'wb'); + $ofp = tmpfile(); if (!is_resource($ofp)) { fclose($ifp); - throw new SodiumException('Could not open output file for writing'); + fclose($destination); + throw new SodiumException('Could not open temporary output file for writing'); } - $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $keypair); + try { + $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $keypair); + self::commitAuthenticatedOutput($ofp, $destination); + } catch (Exception $ex) { + fclose($ifp); + fclose($ofp); + fclose($destination); + throw $ex; + } fclose($ifp); fclose($ofp); + fclose($destination); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); @@ -324,20 +340,13 @@ public static function box_seal_open( throw new SodiumException('Could not open input file for reading'); } - /** @var resource $ofp */ - $ofp = @fopen($outputFile, 'wb'); - if (!is_resource($ofp)) { - fclose($ifp); - throw new SodiumException('Could not open output file for writing'); - } - $ephemeralPK = fread($ifp, ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES); if (!is_string($ephemeralPK)) { + fclose($ifp); throw new SodiumException('Could not read input file'); } if (self::strlen($ephemeralPK) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_PUBLICKEYBYTES) { fclose($ifp); - fclose($ofp); throw new SodiumException('Could not read public key from sealed file'); } @@ -351,9 +360,32 @@ public static function box_seal_open( $ephemeralPK ); - $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $msgKeypair); + /** @var resource $destination */ + $destination = self::openOutputFile($outputFile); + if (!is_resource($destination)) { + fclose($ifp); + throw new SodiumException('Could not open output file for writing'); + } + /** @var resource $ofp */ + $ofp = tmpfile(); + if (!is_resource($ofp)) { + fclose($ifp); + fclose($destination); + throw new SodiumException('Could not open temporary output file for writing'); + } + + try { + $res = self::box_decrypt($ifp, $ofp, $size, $nonce, $msgKeypair); + self::commitAuthenticatedOutput($ofp, $destination); + } catch (Exception $ex) { + fclose($ifp); + fclose($ofp); + fclose($destination); + throw $ex; + } fclose($ifp); fclose($ofp); + fclose($destination); try { ParagonIE_Sodium_Compat::memzero($nonce); ParagonIE_Sodium_Compat::memzero($ephKeypair); @@ -579,16 +611,32 @@ public static function secretbox_open( throw new SodiumException('Could not open input file for reading'); } + /** @var resource $destination */ + $destination = self::openOutputFile($outputFile); + if (!is_resource($destination)) { + fclose($ifp); + throw new SodiumException('Could not open output file for writing'); + } /** @var resource $ofp */ - $ofp = @fopen($outputFile, 'wb'); + $ofp = tmpfile(); if (!is_resource($ofp)) { fclose($ifp); - throw new SodiumException('Could not open output file for writing'); + fclose($destination); + throw new SodiumException('Could not open temporary output file for writing'); } - $res = self::secretbox_decrypt($ifp, $ofp, $size, $nonce, $key); + try { + $res = self::secretbox_decrypt($ifp, $ofp, $size, $nonce, $key); + self::commitAuthenticatedOutput($ofp, $destination); + } catch (Exception $ex) { + fclose($ifp); + fclose($ofp); + fclose($destination); + throw $ex; + } fclose($ifp); fclose($ofp); + fclose($destination); try { ParagonIE_Sodium_Compat::memzero($key); } catch (SodiumException $ex) { @@ -785,36 +833,41 @@ public static function verify( // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; + try { + if (ParagonIE_Sodium_Core_Ed25519::small_order($publicKey)) { + throw new SodiumException('Public key has small order'); + } + /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */ + $A = ParagonIE_Sodium_Core_Ed25519::ge_frombytes_negate_vartime($publicKey); + if (!ParagonIE_Sodium_Core_Ed25519::is_on_main_subgroup($A)) { + throw new SodiumException('Public key is not on a member of the main subgroup'); + } - if (ParagonIE_Sodium_Core_Ed25519::small_order($publicKey)) { - throw new SodiumException('Public key has small order'); - } - /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P3 $A */ - $A = ParagonIE_Sodium_Core_Ed25519::ge_frombytes_negate_vartime($publicKey); - if (!ParagonIE_Sodium_Core_Ed25519::is_on_main_subgroup($A)) { - throw new SodiumException('Public key is not on a member of the main subgroup'); - } - - $hs = hash_init('sha512'); - self::hash_update($hs, self::substr($sig, 0, 32)); - self::hash_update($hs, self::substr($publicKey, 0, 32)); - /** @var resource $hs */ - $hs = self::updateHashWithFile($hs, $fp, $size); - /** @var string $hDigest */ - $hDigest = hash_final($hs, true); - - /** @var string $h */ - $h = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); - - /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ - $R = ParagonIE_Sodium_Core_Ed25519::ge_double_scalarmult_vartime( - $h, - $A, - self::substr($sig, 32) - ); + $hs = hash_init('sha512'); + self::hash_update($hs, self::substr($sig, 0, 32)); + self::hash_update($hs, self::substr($publicKey, 0, 32)); + /** @var resource $hs */ + $hs = self::updateHashWithFile($hs, $fp, $size); + /** @var string $hDigest */ + $hDigest = hash_final($hs, true); + + /** @var string $h */ + $h = ParagonIE_Sodium_Core_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); + + /** @var ParagonIE_Sodium_Core_Curve25519_Ge_P2 $R */ + $R = ParagonIE_Sodium_Core_Ed25519::ge_double_scalarmult_vartime( + $h, + $A, + self::substr($sig, 32) + ); - /** @var string $rcheck */ - $rcheck = ParagonIE_Sodium_Core_Ed25519::ge_tobytes($R); + /** @var string $rcheck */ + $rcheck = ParagonIE_Sodium_Core_Ed25519::ge_tobytes($R); + } catch (Exception $ex) { + fclose($fp); + ParagonIE_Sodium_Compat::$fastMult = $orig; + throw $ex; + } // Close the file handle fclose($fp); @@ -1147,6 +1200,35 @@ protected static function onetimeauth_verify( return $res; } + /** + * @param string $outputFile + * @return resource|bool + */ + protected static function openOutputFile($outputFile) + { + $destination = @fopen($outputFile, 'r+b'); + return is_resource($destination) ? $destination : @fopen($outputFile, 'x+b'); + } + + /** + * @param resource $tmp + * @param resource $destination + * @return void + * @throws SodiumException + */ + protected static function commitAuthenticatedOutput($tmp, $destination) + { + if (fseek($tmp, 0) !== 0) { + throw new SodiumException('Could not rewind temporary output file'); + } + if (fseek($destination, 0) !== 0 || !ftruncate($destination, 0)) { + throw new SodiumException('Could not prepare output file for writing'); + } + if (stream_copy_to_stream($tmp, $destination) === false) { + throw new SodiumException('Could not write to output file'); + } + } + /** * Update a hash context with the contents of a file, without * loading the entire file into memory. @@ -1328,36 +1410,41 @@ public static function verify_core32($sig, $filePath, $publicKey) // Set ParagonIE_Sodium_Compat::$fastMult to true to speed up verification. ParagonIE_Sodium_Compat::$fastMult = true; + try { + if (ParagonIE_Sodium_Core32_Ed25519::small_order($publicKey)) { + throw new SodiumException('Public key has small order'); + } + /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */ + $A = ParagonIE_Sodium_Core32_Ed25519::ge_frombytes_negate_vartime($publicKey); + if (!ParagonIE_Sodium_Core32_Ed25519::is_on_main_subgroup($A)) { + throw new SodiumException('Public key is not on a member of the main subgroup'); + } - if (ParagonIE_Sodium_Core32_Ed25519::small_order($publicKey)) { - throw new SodiumException('Public key has small order'); - } - /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P3 $A */ - $A = ParagonIE_Sodium_Core32_Ed25519::ge_frombytes_negate_vartime($publicKey); - if (!ParagonIE_Sodium_Core32_Ed25519::is_on_main_subgroup($A)) { - throw new SodiumException('Public key is not on a member of the main subgroup'); - } - - $hs = hash_init('sha512'); - self::hash_update($hs, self::substr($sig, 0, 32)); - self::hash_update($hs, self::substr($publicKey, 0, 32)); - /** @var resource $hs */ - $hs = self::updateHashWithFile($hs, $fp, $size); - /** @var string $hDigest */ - $hDigest = hash_final($hs, true); - - /** @var string $h */ - $h = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); - - /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ - $R = ParagonIE_Sodium_Core32_Ed25519::ge_double_scalarmult_vartime( - $h, - $A, - self::substr($sig, 32) - ); + $hs = hash_init('sha512'); + self::hash_update($hs, self::substr($sig, 0, 32)); + self::hash_update($hs, self::substr($publicKey, 0, 32)); + /** @var resource $hs */ + $hs = self::updateHashWithFile($hs, $fp, $size); + /** @var string $hDigest */ + $hDigest = hash_final($hs, true); + + /** @var string $h */ + $h = ParagonIE_Sodium_Core32_Ed25519::sc_reduce($hDigest) . self::substr($hDigest, 32); + + /** @var ParagonIE_Sodium_Core32_Curve25519_Ge_P2 $R */ + $R = ParagonIE_Sodium_Core32_Ed25519::ge_double_scalarmult_vartime( + $h, + $A, + self::substr($sig, 32) + ); - /** @var string $rcheck */ - $rcheck = ParagonIE_Sodium_Core32_Ed25519::ge_tobytes($R); + /** @var string $rcheck */ + $rcheck = ParagonIE_Sodium_Core32_Ed25519::ge_tobytes($R); + } catch (Exception $ex) { + fclose($fp); + ParagonIE_Sodium_Compat::$fastMult = $orig; + throw $ex; + } // Close the file handle fclose($fp);