diff --git a/lib/php72compat.php b/lib/php72compat.php index d7b940e9..bf1e05eb 100644 --- a/lib/php72compat.php +++ b/lib/php72compat.php @@ -1286,13 +1286,14 @@ function sodium_crypto_stream_xor($message, $nonce, $key) /** * @see ParagonIE_Sodium_Compat::hex2bin() * @param string $string + * @param string $ignore * @return string * @throws SodiumException * @throws TypeError */ - function sodium_hex2bin($string) + function sodium_hex2bin($string, $ignore = '') { - return ParagonIE_Sodium_Compat::hex2bin($string); + return ParagonIE_Sodium_Compat::hex2bin($string, $ignore); } } if (!is_callable('sodium_increment')) { diff --git a/src/Compat.php b/src/Compat.php index 7265d45c..3afe97c0 100644 --- a/src/Compat.php +++ b/src/Compat.php @@ -3219,26 +3219,28 @@ public static function crypto_stream_xchacha20_keygen() * Cache-timing-safe implementation of hex2bin(). * * @param string $string Hexadecimal string + * @param string $ignore List of characters to ignore; useful for whitespace * @return string Raw binary string * @throws SodiumException * @throws TypeError * @psalm-suppress TooFewArguments * @psalm-suppress MixedArgument */ - public static function hex2bin($string) + public static function hex2bin($string, $ignore = '') { /* Type checks: */ ParagonIE_Sodium_Core_Util::declareScalarType($string, 'string', 1); + ParagonIE_Sodium_Core_Util::declareScalarType($ignore, 'string', 2); if (self::useNewSodiumAPI()) { if (is_callable('sodium_hex2bin')) { - return (string) sodium_hex2bin($string); + return (string) sodium_hex2bin($string, $ignore); } } if (self::use_fallback('hex2bin')) { - return (string) call_user_func('\\Sodium\\hex2bin', $string); + return (string) call_user_func('\\Sodium\\hex2bin', $string, $ignore); } - return ParagonIE_Sodium_Core_Util::hex2bin($string); + return ParagonIE_Sodium_Core_Util::hex2bin($string, $ignore); } /** diff --git a/src/Core/Util.php b/src/Core/Util.php index 13221b36..73e463f2 100644 --- a/src/Core/Util.php +++ b/src/Core/Util.php @@ -309,27 +309,26 @@ protected static function hash_update(&$hs, $data) * @internal You should not use this directly from another application * * @param string $hexString + * @param string $ignore * @param bool $strictPadding * @return string (raw binary) * @throws RangeException * @throws TypeError */ - public static function hex2bin($hexString, $strictPadding = false) + public static function hex2bin($hexString, $ignore = '', $strictPadding = false) { /* Type checks: */ if (!is_string($hexString)) { throw new TypeError('Argument 1 must be a string, ' . gettype($hexString) . ' given.'); } + if (!is_string($ignore)) { + throw new TypeError('Argument 2 must be a string, ' . gettype($hexString) . ' given.'); + } - /** @var int $hex_pos */ $hex_pos = 0; - /** @var string $bin */ $bin = ''; - /** @var int $c_acc */ $c_acc = 0; - /** @var int $hex_len */ $hex_len = self::strlen($hexString); - /** @var int $state */ $state = 0; if (($hex_len & 1) !== 0) { if ($strictPadding) { @@ -347,20 +346,18 @@ public static function hex2bin($hexString, $strictPadding = false) ++$hex_pos; /** @var int $c */ $c = $chunk[$hex_pos]; - /** @var int $c_num */ $c_num = $c ^ 48; - /** @var int $c_num0 */ $c_num0 = ($c_num - 10) >> 8; - /** @var int $c_alpha */ $c_alpha = ($c & ~32) - 55; - /** @var int $c_alpha0 */ $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; if (($c_num0 | $c_alpha0) === 0) { + if ($ignore && $state === 0 && strpos($ignore, self::intToChr($c)) !== false) { + continue; + } throw new RangeException( 'hex2bin() only expects hexadecimal characters' ); } - /** @var int $c_val */ $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); if ($state === 0) { $c_acc = $c_val * 16; @@ -382,7 +379,6 @@ public static function hex2bin($hexString, $strictPadding = false) */ public static function intArrayToString(array $ints) { - /** @var array $args */ $args = $ints; foreach ($args as $i => $v) { $args[$i] = (int) ($v & 0xff); diff --git a/tests/unit/HexTest.php b/tests/unit/HexTest.php new file mode 100644 index 00000000..7fbbeacd --- /dev/null +++ b/tests/unit/HexTest.php @@ -0,0 +1,32 @@ +assertFalse($fail, 'This should have failed but did not!'); + $this->assertSame($binary, $decoded, 'Binary mismatch'); + } catch (RangeException $ex) { + $this->assertTrue($fail, 'An unexpected hex2bin failure occurred'); + } catch (SodiumException $ex) { + $this->assertTrue($fail, 'An unexpected hex2bin failure occurred'); + } + } +}