From 2d93c737399c5a20b3bc4180f3e1d34251aaed9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tinjo=20Sch=C3=B6ni?= <32767367+tscni@users.noreply.github.com> Date: Sat, 27 Sep 2025 16:02:46 +0200 Subject: [PATCH] Improve Base64UrlSafe performance when ext-sodium is installed These adjustments are only applying the changes made in paragonie/constant_time_encoding between 2.6.3 and 2.8.2 See: https://github.com/paragonie/constant_time_encoding/compare/v2.6.3..v2.8.2 --- src/Library/Core/Util/Base64UrlSafe.php | 56 +++++++++++++++++++------ 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/src/Library/Core/Util/Base64UrlSafe.php b/src/Library/Core/Util/Base64UrlSafe.php index 8ab1b28e..4be5faed 100644 --- a/src/Library/Core/Util/Base64UrlSafe.php +++ b/src/Library/Core/Util/Base64UrlSafe.php @@ -6,7 +6,18 @@ use InvalidArgumentException; use RangeException; +use SensitiveParameter; +use SodiumException; +use function extension_loaded; +use function pack; +use function rtrim; +use function sodium_base642bin; +use function sodium_bin2base64; use function strlen; +use function substr; +use function unpack; +use const SODIUM_BASE64_VARIANT_URLSAFE; +use const SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING; /** * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. @@ -33,17 +44,31 @@ final readonly class Base64UrlSafe { - public static function encode(string $binString): string + public static function encode(#[SensitiveParameter] string $binString): string { + if (extension_loaded('sodium')) { + try { + return sodium_bin2base64($binString, SODIUM_BASE64_VARIANT_URLSAFE); + } catch (SodiumException $ex) { + throw new RangeException($ex->getMessage(), $ex->getCode(), $ex); + } + } return static::doEncode($binString, true); } - public static function encodeUnpadded(string $src): string + public static function encodeUnpadded(#[SensitiveParameter] string $src): string { + if (extension_loaded('sodium')) { + try { + return sodium_bin2base64($src, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING); + } catch (SodiumException $ex) { + throw new RangeException($ex->getMessage(), $ex->getCode(), $ex); + } + } return static::doEncode($src, false); } - public static function decode(string $encodedString, bool $strictPadding = false): string + public static function decode(#[SensitiveParameter] string $encodedString, bool $strictPadding = false): string { $srcLen = self::safeStrlen($encodedString); if ($srcLen === 0) { @@ -65,6 +90,16 @@ public static function decode(string $encodedString, bool $strictPadding = false if ($encodedString[$srcLen - 1] === '=') { throw new RangeException('Incorrect padding'); } + if (extension_loaded('sodium')) { + try { + return sodium_base642bin( + self::safeSubstr($encodedString, 0, $srcLen), + SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING + ); + } catch (SodiumException $ex) { + throw new RangeException($ex->getMessage(), $ex->getCode(), $ex); + } + } } else { $encodedString = rtrim($encodedString, '='); $srcLen = self::safeStrlen($encodedString); @@ -120,26 +155,21 @@ public static function decode(string $encodedString, bool $strictPadding = false return $dest; } - public static function decodeNoPadding(string $encodedString): string + public static function decodeNoPadding(#[SensitiveParameter] string $encodedString): string { $srcLen = self::safeStrlen($encodedString); if ($srcLen === 0) { return ''; } if (($srcLen & 3) === 0) { - if ($encodedString[$srcLen - 1] === '=') { + if ($encodedString[$srcLen - 1] === '=' || $encodedString[$srcLen - 2] === '=') { throw new InvalidArgumentException("decodeNoPadding() doesn't tolerate padding"); } - if (($srcLen & 3) > 1) { - if ($encodedString[$srcLen - 2] === '=') { - throw new InvalidArgumentException("decodeNoPadding() doesn't tolerate padding"); - } - } } return static::decode($encodedString, true); } - private static function doEncode(string $src, bool $pad = true): string + private static function doEncode(#[SensitiveParameter] string $src, bool $pad = true): string { $dest = ''; $srcLen = self::safeStrlen($src); @@ -204,12 +234,12 @@ private static function encode6Bits(int $src): string return pack('C', $src + $diff); } - private static function safeStrlen(string $str): int + private static function safeStrlen(#[SensitiveParameter] string $str): int { return strlen($str); } - private static function safeSubstr(string $str, int $start = 0, $length = null): string + private static function safeSubstr(#[SensitiveParameter] string $str, int $start = 0, $length = null): string { if ($length === 0) { return '';