├── LICENSE.txt ├── README.md ├── composer.json └── src ├── Base32.php ├── Base32Hex.php ├── Base64.php ├── Base64DotSlash.php ├── Base64DotSlashOrdered.php ├── Base64UrlSafe.php ├── Binary.php ├── EncoderInterface.php ├── Encoding.php ├── Hex.php └── RFC4648.php /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 - 2022 Paragon Initiative Enterprises 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | ------------------------------------------------------------------------------ 24 | This library was based on the work of Steve "Sc00bz" Thomas. 25 | ------------------------------------------------------------------------------ 26 | 27 | The MIT License (MIT) 28 | 29 | Copyright (c) 2014 Steve Thomas 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in all 39 | copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 47 | SOFTWARE. 48 | 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Constant-Time Encoding 2 | 3 | [![Build Status](https://github.com/paragonie/constant_time_encoding/actions/workflows/ci.yml/badge.svg)](https://github.com/paragonie/constant_time_encoding/actions) 4 | [![Static Analysis](https://github.com/paragonie/constant_time_encoding/actions/workflows/psalm.yml/badge.svg)](https://github.com/paragonie/constant_time_encoding/actions) 5 | [![Latest Stable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/stable)](https://packagist.org/packages/paragonie/constant_time_encoding) 6 | [![Latest Unstable Version](https://poser.pugx.org/paragonie/constant_time_encoding/v/unstable)](https://packagist.org/packages/paragonie/constant_time_encoding) 7 | [![License](https://poser.pugx.org/paragonie/constant_time_encoding/license)](https://packagist.org/packages/paragonie/constant_time_encoding) 8 | [![Downloads](https://img.shields.io/packagist/dt/paragonie/constant_time_encoding.svg)](https://packagist.org/packages/paragonie/constant_time_encoding) 9 | 10 | Based on the [constant-time base64 implementation made by Steve "Sc00bz" Thomas](https://github.com/Sc00bz/ConstTimeEncoding), 11 | this library aims to offer character encoding functions that do not leak 12 | information about what you are encoding/decoding via processor cache 13 | misses. Further reading on [cache-timing attacks](http://blog.ircmaxell.com/2014/11/its-all-about-time.html). 14 | 15 | Our fork offers the following enhancements: 16 | 17 | * `mbstring.func_overload` resistance 18 | * Unit tests 19 | * Composer- and Packagist-ready 20 | * Base16 encoding 21 | * Base32 encoding 22 | * Uses `pack()` and `unpack()` instead of `chr()` and `ord()` 23 | 24 | ## PHP Version Requirements 25 | 26 | Version 3 of this library should work on **PHP 8** or newer. 27 | 28 | Version 2 of this library should work on **PHP 7** or newer. See [the v2.x branch](https://github.com/paragonie/constant_time_encoding/tree/v2.x). 29 | 30 | For PHP 5 support, see [the v1.x branch](https://github.com/paragonie/constant_time_encoding/tree/v1.x). 31 | 32 | If you are adding this as a dependency to a project intended to work on PHP 5 through 8.4, please set the required version to `^1|^2|^3`. 33 | 34 | ## How to Install 35 | 36 | ```sh 37 | composer require paragonie/constant_time_encoding 38 | ``` 39 | 40 | ## How to Use 41 | 42 | ```php 43 | use ParagonIE\ConstantTime\Encoding; 44 | 45 | // possibly (if applicable): 46 | // require 'vendor/autoload.php'; 47 | 48 | $data = random_bytes(32); 49 | echo Encoding::base64Encode($data), "\n"; 50 | echo Encoding::base32EncodeUpper($data), "\n"; 51 | echo Encoding::base32Encode($data), "\n"; 52 | echo Encoding::hexEncode($data), "\n"; 53 | echo Encoding::hexEncodeUpper($data), "\n"; 54 | ``` 55 | 56 | Example output: 57 | 58 | ``` 59 | 1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE= 60 | 2VMKKPSHSWVCVZJ6E7SONRY3ZXCNG3GE6ZZFU7TGJSX7KUKFNLAQ==== 61 | 2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq==== 62 | d558a53e4795aa2ae53e27e4e6c71bcdc4d36cc4f6725a7e664caff551456ac1 63 | D558A53E4795AA2AE53E27E4E6C71BDCC4D36CC4F6725A7E664CAFF551456AC1 64 | ``` 65 | 66 | If you only need a particular variant, you can just reference the 67 | required class like so: 68 | 69 | ```php 70 | use ParagonIE\ConstantTime\Base64; 71 | use ParagonIE\ConstantTime\Base32; 72 | 73 | $data = random_bytes(32); 74 | echo Base64::encode($data), "\n"; 75 | echo Base32::encode($data), "\n"; 76 | ``` 77 | 78 | Example output: 79 | 80 | ``` 81 | 1VilPkeVqirlPifk5scbzcTTbMT2clp+Zkyv9VFFasE= 82 | 2vmkkpshswvcvzj6e7sonry3zxcng3ge6zzfu7tgjsx7kukfnlaq==== 83 | ``` 84 | 85 | ## Support Contracts 86 | 87 | If your company uses this library in their products or services, you may be 88 | interested in [purchasing a support contract from Paragon Initiative Enterprises](https://paragonie.com/enterprise). 89 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paragonie/constant_time_encoding", 3 | "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", 4 | "keywords": [ 5 | "base64", 6 | "encoding", 7 | "rfc4648", 8 | "base32", 9 | "base16", 10 | "hex", 11 | "bin2hex", 12 | "hex2bin", 13 | "base64_encode", 14 | "base64_decode", 15 | "base32_encode", 16 | "base32_decode" 17 | ], 18 | "license": "MIT", 19 | "type": "library", 20 | "authors": [ 21 | { 22 | "name": "Paragon Initiative Enterprises", 23 | "email": "security@paragonie.com", 24 | "homepage": "https://paragonie.com", 25 | "role": "Maintainer" 26 | }, 27 | { 28 | "name": "Steve 'Sc00bz' Thomas", 29 | "email": "steve@tobtu.com", 30 | "homepage": "https://www.tobtu.com", 31 | "role": "Original Developer" 32 | } 33 | ], 34 | "support": { 35 | "issues": "https://github.com/paragonie/constant_time_encoding/issues", 36 | "email": "info@paragonie.com", 37 | "source": "https://github.com/paragonie/constant_time_encoding" 38 | }, 39 | "require": { 40 | "php": "^8" 41 | }, 42 | "require-dev": { 43 | "phpunit/phpunit": "^9", 44 | "vimeo/psalm": "^4|^5" 45 | }, 46 | "autoload": { 47 | "psr-4": { 48 | "ParagonIE\\ConstantTime\\": "src/" 49 | } 50 | }, 51 | "autoload-dev": { 52 | "psr-4": { 53 | "ParagonIE\\ConstantTime\\Tests\\": "tests/" 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Base32.php: -------------------------------------------------------------------------------- 1 | 96 && $src < 123) $ret += $src - 97 + 1; // -64 138 | $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96); 139 | 140 | // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 141 | $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 142 | 143 | return $ret; 144 | } 145 | 146 | /** 147 | * Uses bitwise operators instead of table-lookups to turn 5-bit integers 148 | * into 8-bit integers. 149 | * 150 | * Uppercase variant. 151 | * 152 | * @param int $src 153 | * @return int 154 | */ 155 | protected static function decode5BitsUpper(int $src): int 156 | { 157 | $ret = -1; 158 | 159 | // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64 160 | $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); 161 | 162 | // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 163 | $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 164 | 165 | return $ret; 166 | } 167 | 168 | /** 169 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 170 | * into 5-bit integers. 171 | * 172 | * @param int $src 173 | * @return string 174 | */ 175 | protected static function encode5Bits(int $src): string 176 | { 177 | $diff = 0x61; 178 | 179 | // if ($src > 25) $ret -= 72; 180 | $diff -= ((25 - $src) >> 8) & 73; 181 | 182 | return \pack('C', $src + $diff); 183 | } 184 | 185 | /** 186 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 187 | * into 5-bit integers. 188 | * 189 | * Uppercase variant. 190 | * 191 | * @param int $src 192 | * @return string 193 | */ 194 | protected static function encode5BitsUpper(int $src): string 195 | { 196 | $diff = 0x41; 197 | 198 | // if ($src > 25) $ret -= 40; 199 | $diff -= ((25 - $src) >> 8) & 41; 200 | 201 | return \pack('C', $src + $diff); 202 | } 203 | 204 | /** 205 | * @param string $encodedString 206 | * @param bool $upper 207 | * @return string 208 | */ 209 | public static function decodeNoPadding( 210 | #[\SensitiveParameter] 211 | string $encodedString, 212 | bool $upper = false 213 | ): string { 214 | $srcLen = Binary::safeStrlen($encodedString); 215 | if ($srcLen === 0) { 216 | return ''; 217 | } 218 | if (($srcLen & 7) === 0) { 219 | for ($j = 0; $j < 7 && $j < $srcLen; ++$j) { 220 | if ($encodedString[$srcLen - $j - 1] === '=') { 221 | throw new InvalidArgumentException( 222 | "decodeNoPadding() doesn't tolerate padding" 223 | ); 224 | } 225 | } 226 | } 227 | return static::doDecode( 228 | $encodedString, 229 | $upper, 230 | true 231 | ); 232 | } 233 | 234 | /** 235 | * Base32 decoding 236 | * 237 | * @param string $src 238 | * @param bool $upper 239 | * @param bool $strictPadding 240 | * @return string 241 | * 242 | * @throws TypeError 243 | */ 244 | protected static function doDecode( 245 | #[\SensitiveParameter] 246 | string $src, 247 | bool $upper = false, 248 | bool $strictPadding = false 249 | ): string { 250 | // We do this to reduce code duplication: 251 | $method = $upper 252 | ? 'decode5BitsUpper' 253 | : 'decode5Bits'; 254 | 255 | // Remove padding 256 | $srcLen = Binary::safeStrlen($src); 257 | if ($srcLen === 0) { 258 | return ''; 259 | } 260 | if ($strictPadding) { 261 | if (($srcLen & 7) === 0) { 262 | for ($j = 0; $j < 7; ++$j) { 263 | if ($src[$srcLen - 1] === '=') { 264 | $srcLen--; 265 | } else { 266 | break; 267 | } 268 | } 269 | } 270 | if (($srcLen & 7) === 1) { 271 | throw new RangeException( 272 | 'Incorrect padding' 273 | ); 274 | } 275 | } else { 276 | $src = \rtrim($src, '='); 277 | $srcLen = Binary::safeStrlen($src); 278 | } 279 | 280 | $err = 0; 281 | $dest = ''; 282 | // Main loop (no padding): 283 | for ($i = 0; $i + 8 <= $srcLen; $i += 8) { 284 | /** @var array $chunk */ 285 | $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8)); 286 | /** @var int $c0 */ 287 | $c0 = static::$method($chunk[1]); 288 | /** @var int $c1 */ 289 | $c1 = static::$method($chunk[2]); 290 | /** @var int $c2 */ 291 | $c2 = static::$method($chunk[3]); 292 | /** @var int $c3 */ 293 | $c3 = static::$method($chunk[4]); 294 | /** @var int $c4 */ 295 | $c4 = static::$method($chunk[5]); 296 | /** @var int $c5 */ 297 | $c5 = static::$method($chunk[6]); 298 | /** @var int $c6 */ 299 | $c6 = static::$method($chunk[7]); 300 | /** @var int $c7 */ 301 | $c7 = static::$method($chunk[8]); 302 | 303 | $dest .= \pack( 304 | 'CCCCC', 305 | (($c0 << 3) | ($c1 >> 2) ) & 0xff, 306 | (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 307 | (($c3 << 4) | ($c4 >> 1) ) & 0xff, 308 | (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff, 309 | (($c6 << 5) | ($c7 ) ) & 0xff 310 | ); 311 | $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8; 312 | } 313 | // The last chunk, which may have padding: 314 | if ($i < $srcLen) { 315 | /** @var array $chunk */ 316 | $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); 317 | /** @var int $c0 */ 318 | $c0 = static::$method($chunk[1]); 319 | 320 | if ($i + 6 < $srcLen) { 321 | /** @var int $c1 */ 322 | $c1 = static::$method($chunk[2]); 323 | /** @var int $c2 */ 324 | $c2 = static::$method($chunk[3]); 325 | /** @var int $c3 */ 326 | $c3 = static::$method($chunk[4]); 327 | /** @var int $c4 */ 328 | $c4 = static::$method($chunk[5]); 329 | /** @var int $c5 */ 330 | $c5 = static::$method($chunk[6]); 331 | /** @var int $c6 */ 332 | $c6 = static::$method($chunk[7]); 333 | 334 | $dest .= \pack( 335 | 'CCCC', 336 | (($c0 << 3) | ($c1 >> 2) ) & 0xff, 337 | (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 338 | (($c3 << 4) | ($c4 >> 1) ) & 0xff, 339 | (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff 340 | ); 341 | $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8; 342 | if ($strictPadding) { 343 | $err |= ($c6 << 5) & 0xff; 344 | } 345 | } elseif ($i + 5 < $srcLen) { 346 | /** @var int $c1 */ 347 | $c1 = static::$method($chunk[2]); 348 | /** @var int $c2 */ 349 | $c2 = static::$method($chunk[3]); 350 | /** @var int $c3 */ 351 | $c3 = static::$method($chunk[4]); 352 | /** @var int $c4 */ 353 | $c4 = static::$method($chunk[5]); 354 | /** @var int $c5 */ 355 | $c5 = static::$method($chunk[6]); 356 | 357 | $dest .= \pack( 358 | 'CCCC', 359 | (($c0 << 3) | ($c1 >> 2) ) & 0xff, 360 | (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 361 | (($c3 << 4) | ($c4 >> 1) ) & 0xff, 362 | (($c4 << 7) | ($c5 << 2) ) & 0xff 363 | ); 364 | $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8; 365 | } elseif ($i + 4 < $srcLen) { 366 | /** @var int $c1 */ 367 | $c1 = static::$method($chunk[2]); 368 | /** @var int $c2 */ 369 | $c2 = static::$method($chunk[3]); 370 | /** @var int $c3 */ 371 | $c3 = static::$method($chunk[4]); 372 | /** @var int $c4 */ 373 | $c4 = static::$method($chunk[5]); 374 | 375 | $dest .= \pack( 376 | 'CCC', 377 | (($c0 << 3) | ($c1 >> 2) ) & 0xff, 378 | (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 379 | (($c3 << 4) | ($c4 >> 1) ) & 0xff 380 | ); 381 | $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8; 382 | if ($strictPadding) { 383 | $err |= ($c4 << 7) & 0xff; 384 | } 385 | } elseif ($i + 3 < $srcLen) { 386 | /** @var int $c1 */ 387 | $c1 = static::$method($chunk[2]); 388 | /** @var int $c2 */ 389 | $c2 = static::$method($chunk[3]); 390 | /** @var int $c3 */ 391 | $c3 = static::$method($chunk[4]); 392 | 393 | $dest .= \pack( 394 | 'CC', 395 | (($c0 << 3) | ($c1 >> 2) ) & 0xff, 396 | (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff 397 | ); 398 | $err |= ($c0 | $c1 | $c2 | $c3) >> 8; 399 | if ($strictPadding) { 400 | $err |= ($c3 << 4) & 0xff; 401 | } 402 | } elseif ($i + 2 < $srcLen) { 403 | /** @var int $c1 */ 404 | $c1 = static::$method($chunk[2]); 405 | /** @var int $c2 */ 406 | $c2 = static::$method($chunk[3]); 407 | 408 | $dest .= \pack( 409 | 'CC', 410 | (($c0 << 3) | ($c1 >> 2) ) & 0xff, 411 | (($c1 << 6) | ($c2 << 1) ) & 0xff 412 | ); 413 | $err |= ($c0 | $c1 | $c2) >> 8; 414 | if ($strictPadding) { 415 | $err |= ($c2 << 6) & 0xff; 416 | } 417 | } elseif ($i + 1 < $srcLen) { 418 | /** @var int $c1 */ 419 | $c1 = static::$method($chunk[2]); 420 | 421 | $dest .= \pack( 422 | 'C', 423 | (($c0 << 3) | ($c1 >> 2) ) & 0xff 424 | ); 425 | $err |= ($c0 | $c1) >> 8; 426 | if ($strictPadding) { 427 | $err |= ($c1 << 6) & 0xff; 428 | } 429 | } else { 430 | $dest .= \pack( 431 | 'C', 432 | (($c0 << 3) ) & 0xff 433 | ); 434 | $err |= ($c0) >> 8; 435 | } 436 | } 437 | $check = ($err === 0); 438 | if (!$check) { 439 | throw new RangeException( 440 | 'Base32::doDecode() only expects characters in the correct base32 alphabet' 441 | ); 442 | } 443 | return $dest; 444 | } 445 | 446 | /** 447 | * Base32 Encoding 448 | * 449 | * @param string $src 450 | * @param bool $upper 451 | * @param bool $pad 452 | * @return string 453 | * @throws TypeError 454 | */ 455 | protected static function doEncode( 456 | #[\SensitiveParameter] 457 | string $src, 458 | bool $upper = false, 459 | bool $pad = true 460 | ): string { 461 | // We do this to reduce code duplication: 462 | $method = $upper 463 | ? 'encode5BitsUpper' 464 | : 'encode5Bits'; 465 | 466 | $dest = ''; 467 | $srcLen = Binary::safeStrlen($src); 468 | 469 | // Main loop (no padding): 470 | for ($i = 0; $i + 5 <= $srcLen; $i += 5) { 471 | /** @var array $chunk */ 472 | $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5)); 473 | $b0 = $chunk[1]; 474 | $b1 = $chunk[2]; 475 | $b2 = $chunk[3]; 476 | $b3 = $chunk[4]; 477 | $b4 = $chunk[5]; 478 | $dest .= 479 | static::$method( ($b0 >> 3) & 31) . 480 | static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 481 | static::$method((($b1 >> 1) ) & 31) . 482 | static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 483 | static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 484 | static::$method((($b3 >> 2) ) & 31) . 485 | static::$method((($b3 << 3) | ($b4 >> 5)) & 31) . 486 | static::$method( $b4 & 31); 487 | } 488 | // The last chunk, which may have padding: 489 | if ($i < $srcLen) { 490 | /** @var array $chunk */ 491 | $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); 492 | $b0 = $chunk[1]; 493 | if ($i + 3 < $srcLen) { 494 | $b1 = $chunk[2]; 495 | $b2 = $chunk[3]; 496 | $b3 = $chunk[4]; 497 | $dest .= 498 | static::$method( ($b0 >> 3) & 31) . 499 | static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 500 | static::$method((($b1 >> 1) ) & 31) . 501 | static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 502 | static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 503 | static::$method((($b3 >> 2) ) & 31) . 504 | static::$method((($b3 << 3) ) & 31); 505 | if ($pad) { 506 | $dest .= '='; 507 | } 508 | } elseif ($i + 2 < $srcLen) { 509 | $b1 = $chunk[2]; 510 | $b2 = $chunk[3]; 511 | $dest .= 512 | static::$method( ($b0 >> 3) & 31) . 513 | static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 514 | static::$method((($b1 >> 1) ) & 31) . 515 | static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 516 | static::$method((($b2 << 1) ) & 31); 517 | if ($pad) { 518 | $dest .= '==='; 519 | } 520 | } elseif ($i + 1 < $srcLen) { 521 | $b1 = $chunk[2]; 522 | $dest .= 523 | static::$method( ($b0 >> 3) & 31) . 524 | static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 525 | static::$method((($b1 >> 1) ) & 31) . 526 | static::$method((($b1 << 4) ) & 31); 527 | if ($pad) { 528 | $dest .= '===='; 529 | } 530 | } else { 531 | $dest .= 532 | static::$method( ($b0 >> 3) & 31) . 533 | static::$method( ($b0 << 2) & 31); 534 | if ($pad) { 535 | $dest .= '======'; 536 | } 537 | } 538 | } 539 | return $dest; 540 | } 541 | } 542 | -------------------------------------------------------------------------------- /src/Base32Hex.php: -------------------------------------------------------------------------------- 1 | 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47 48 | $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47); 49 | 50 | // if ($src > 0x60 && $src < 0x77) ret += $src - 0x61 + 10 + 1; // -86 51 | $ret += (((0x60 - $src) & ($src - 0x77)) >> 8) & ($src - 86); 52 | 53 | return $ret; 54 | } 55 | 56 | /** 57 | * Uses bitwise operators instead of table-lookups to turn 5-bit integers 58 | * into 8-bit integers. 59 | * 60 | * @param int $src 61 | * @return int 62 | */ 63 | protected static function decode5BitsUpper(int $src): int 64 | { 65 | $ret = -1; 66 | 67 | // if ($src > 0x30 && $src < 0x3a) ret += $src - 0x2e + 1; // -47 68 | $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src - 47); 69 | 70 | // if ($src > 0x40 && $src < 0x57) ret += $src - 0x41 + 10 + 1; // -54 71 | $ret += (((0x40 - $src) & ($src - 0x57)) >> 8) & ($src - 54); 72 | 73 | return $ret; 74 | } 75 | 76 | /** 77 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 78 | * into 5-bit integers. 79 | * 80 | * @param int $src 81 | * @return string 82 | */ 83 | protected static function encode5Bits(int $src): string 84 | { 85 | $src += 0x30; 86 | 87 | // if ($src > 0x39) $src += 0x61 - 0x3a; // 39 88 | $src += ((0x39 - $src) >> 8) & 39; 89 | 90 | return \pack('C', $src); 91 | } 92 | 93 | /** 94 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 95 | * into 5-bit integers. 96 | * 97 | * Uppercase variant. 98 | * 99 | * @param int $src 100 | * @return string 101 | */ 102 | protected static function encode5BitsUpper(int $src): string 103 | { 104 | $src += 0x30; 105 | 106 | // if ($src > 0x39) $src += 0x41 - 0x3a; // 7 107 | $src += ((0x39 - $src) >> 8) & 7; 108 | 109 | return \pack('C', $src); 110 | } 111 | } -------------------------------------------------------------------------------- /src/Base64.php: -------------------------------------------------------------------------------- 1 | $chunk */ 91 | $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 3)); 92 | $b0 = $chunk[1]; 93 | $b1 = $chunk[2]; 94 | $b2 = $chunk[3]; 95 | 96 | $dest .= 97 | static::encode6Bits( $b0 >> 2 ) . 98 | static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . 99 | static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) . 100 | static::encode6Bits( $b2 & 63); 101 | } 102 | // The last chunk, which may have padding: 103 | if ($i < $srcLen) { 104 | /** @var array $chunk */ 105 | $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); 106 | $b0 = $chunk[1]; 107 | if ($i + 1 < $srcLen) { 108 | $b1 = $chunk[2]; 109 | $dest .= 110 | static::encode6Bits($b0 >> 2) . 111 | static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) . 112 | static::encode6Bits(($b1 << 2) & 63); 113 | if ($pad) { 114 | $dest .= '='; 115 | } 116 | } else { 117 | $dest .= 118 | static::encode6Bits( $b0 >> 2) . 119 | static::encode6Bits(($b0 << 4) & 63); 120 | if ($pad) { 121 | $dest .= '=='; 122 | } 123 | } 124 | } 125 | return $dest; 126 | } 127 | 128 | /** 129 | * decode from base64 into binary 130 | * 131 | * Base64 character set "./[A-Z][a-z][0-9]" 132 | * 133 | * @param string $encodedString 134 | * @param bool $strictPadding 135 | * @return string 136 | * 137 | * @throws RangeException 138 | * @throws TypeError 139 | */ 140 | public static function decode( 141 | #[\SensitiveParameter] 142 | string $encodedString, 143 | bool $strictPadding = false 144 | ): string { 145 | // Remove padding 146 | $srcLen = Binary::safeStrlen($encodedString); 147 | if ($srcLen === 0) { 148 | return ''; 149 | } 150 | 151 | if ($strictPadding) { 152 | if (($srcLen & 3) === 0) { 153 | if ($encodedString[$srcLen - 1] === '=') { 154 | $srcLen--; 155 | if ($encodedString[$srcLen - 1] === '=') { 156 | $srcLen--; 157 | } 158 | } 159 | } 160 | if (($srcLen & 3) === 1) { 161 | throw new RangeException( 162 | 'Incorrect padding' 163 | ); 164 | } 165 | if ($encodedString[$srcLen - 1] === '=') { 166 | throw new RangeException( 167 | 'Incorrect padding' 168 | ); 169 | } 170 | } else { 171 | $encodedString = \rtrim($encodedString, '='); 172 | $srcLen = Binary::safeStrlen($encodedString); 173 | } 174 | 175 | $err = 0; 176 | $dest = ''; 177 | // Main loop (no padding): 178 | for ($i = 0; $i + 4 <= $srcLen; $i += 4) { 179 | /** @var array $chunk */ 180 | $chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, 4)); 181 | $c0 = static::decode6Bits($chunk[1]); 182 | $c1 = static::decode6Bits($chunk[2]); 183 | $c2 = static::decode6Bits($chunk[3]); 184 | $c3 = static::decode6Bits($chunk[4]); 185 | 186 | $dest .= \pack( 187 | 'CCC', 188 | ((($c0 << 2) | ($c1 >> 4)) & 0xff), 189 | ((($c1 << 4) | ($c2 >> 2)) & 0xff), 190 | ((($c2 << 6) | $c3 ) & 0xff) 191 | ); 192 | $err |= ($c0 | $c1 | $c2 | $c3) >> 8; 193 | } 194 | // The last chunk, which may have padding: 195 | if ($i < $srcLen) { 196 | /** @var array $chunk */ 197 | $chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, $srcLen - $i)); 198 | $c0 = static::decode6Bits($chunk[1]); 199 | 200 | if ($i + 2 < $srcLen) { 201 | $c1 = static::decode6Bits($chunk[2]); 202 | $c2 = static::decode6Bits($chunk[3]); 203 | $dest .= \pack( 204 | 'CC', 205 | ((($c0 << 2) | ($c1 >> 4)) & 0xff), 206 | ((($c1 << 4) | ($c2 >> 2)) & 0xff) 207 | ); 208 | $err |= ($c0 | $c1 | $c2) >> 8; 209 | if ($strictPadding) { 210 | $err |= ($c2 << 6) & 0xff; 211 | } 212 | } elseif ($i + 1 < $srcLen) { 213 | $c1 = static::decode6Bits($chunk[2]); 214 | $dest .= \pack( 215 | 'C', 216 | ((($c0 << 2) | ($c1 >> 4)) & 0xff) 217 | ); 218 | $err |= ($c0 | $c1) >> 8; 219 | if ($strictPadding) { 220 | $err |= ($c1 << 4) & 0xff; 221 | } 222 | } elseif ($strictPadding) { 223 | $err |= 1; 224 | } 225 | } 226 | $check = ($err === 0); 227 | if (!$check) { 228 | throw new RangeException( 229 | 'Base64::decode() only expects characters in the correct base64 alphabet' 230 | ); 231 | } 232 | return $dest; 233 | } 234 | 235 | /** 236 | * @param string $encodedString 237 | * @return string 238 | */ 239 | public static function decodeNoPadding( 240 | #[\SensitiveParameter] 241 | string $encodedString 242 | ): string { 243 | $srcLen = Binary::safeStrlen($encodedString); 244 | if ($srcLen === 0) { 245 | return ''; 246 | } 247 | if (($srcLen & 3) === 0) { 248 | // If $strLen is not zero, and it is divisible by 4, then it's at least 4. 249 | if ($encodedString[$srcLen - 1] === '=' || $encodedString[$srcLen - 2] === '=') { 250 | throw new InvalidArgumentException( 251 | "decodeNoPadding() doesn't tolerate padding" 252 | ); 253 | } 254 | } 255 | return static::decode( 256 | $encodedString, 257 | true 258 | ); 259 | } 260 | 261 | /** 262 | * Uses bitwise operators instead of table-lookups to turn 6-bit integers 263 | * into 8-bit integers. 264 | * 265 | * Base64 character set: 266 | * [A-Z] [a-z] [0-9] + / 267 | * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f 268 | * 269 | * @param int $src 270 | * @return int 271 | */ 272 | protected static function decode6Bits(int $src): int 273 | { 274 | $ret = -1; 275 | 276 | // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 277 | $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); 278 | 279 | // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 280 | $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); 281 | 282 | // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 283 | $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); 284 | 285 | // if ($src == 0x2b) $ret += 62 + 1; 286 | $ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63; 287 | 288 | // if ($src == 0x2f) ret += 63 + 1; 289 | $ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64; 290 | 291 | return $ret; 292 | } 293 | 294 | /** 295 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 296 | * into 6-bit integers. 297 | * 298 | * @param int $src 299 | * @return string 300 | */ 301 | protected static function encode6Bits(int $src): string 302 | { 303 | $diff = 0x41; 304 | 305 | // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 306 | $diff += ((25 - $src) >> 8) & 6; 307 | 308 | // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 309 | $diff -= ((51 - $src) >> 8) & 75; 310 | 311 | // if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15 312 | $diff -= ((61 - $src) >> 8) & 15; 313 | 314 | // if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3 315 | $diff += ((62 - $src) >> 8) & 3; 316 | 317 | return \pack('C', $src + $diff); 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /src/Base64DotSlash.php: -------------------------------------------------------------------------------- 1 | 0x2d && $src < 0x30) ret += $src - 0x2e + 1; // -45 52 | $ret += (((0x2d - $src) & ($src - 0x30)) >> 8) & ($src - 45); 53 | 54 | // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 2 + 1; // -62 55 | $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 62); 56 | 57 | // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 28 + 1; // -68 58 | $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 68); 59 | 60 | // if ($src > 0x2f && $src < 0x3a) ret += $src - 0x30 + 54 + 1; // 7 61 | $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 7); 62 | 63 | return $ret; 64 | } 65 | 66 | /** 67 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 68 | * into 6-bit integers. 69 | * 70 | * @param int $src 71 | * @return string 72 | */ 73 | protected static function encode6Bits(int $src): string 74 | { 75 | $src += 0x2e; 76 | 77 | // if ($src > 0x2f) $src += 0x41 - 0x30; // 17 78 | $src += ((0x2f - $src) >> 8) & 17; 79 | 80 | // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6 81 | $src += ((0x5a - $src) >> 8) & 6; 82 | 83 | // if ($src > 0x7a) $src += 0x30 - 0x7b; // -75 84 | $src -= ((0x7a - $src) >> 8) & 75; 85 | 86 | return \pack('C', $src); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Base64DotSlashOrdered.php: -------------------------------------------------------------------------------- 1 | 0x2d && $src < 0x3a) ret += $src - 0x2e + 1; // -45 52 | $ret += (((0x2d - $src) & ($src - 0x3a)) >> 8) & ($src - 45); 53 | 54 | // if ($src > 0x40 && $src < 0x5b) ret += $src - 0x41 + 12 + 1; // -52 55 | $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 52); 56 | 57 | // if ($src > 0x60 && $src < 0x7b) ret += $src - 0x61 + 38 + 1; // -58 58 | $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 58); 59 | 60 | return $ret; 61 | } 62 | 63 | /** 64 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 65 | * into 6-bit integers. 66 | * 67 | * @param int $src 68 | * @return string 69 | */ 70 | protected static function encode6Bits(int $src): string 71 | { 72 | $src += 0x2e; 73 | 74 | // if ($src > 0x39) $src += 0x41 - 0x3a; // 7 75 | $src += ((0x39 - $src) >> 8) & 7; 76 | 77 | // if ($src > 0x5a) $src += 0x61 - 0x5b; // 6 78 | $src += ((0x5a - $src) >> 8) & 6; 79 | 80 | return \pack('C', $src); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Base64UrlSafe.php: -------------------------------------------------------------------------------- 1 | 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64 53 | $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); 54 | 55 | // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70 56 | $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70); 57 | 58 | // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5 59 | $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5); 60 | 61 | // if ($src == 0x2c) $ret += 62 + 1; 62 | $ret += (((0x2c - $src) & ($src - 0x2e)) >> 8) & 63; 63 | 64 | // if ($src == 0x5f) ret += 63 + 1; 65 | $ret += (((0x5e - $src) & ($src - 0x60)) >> 8) & 64; 66 | 67 | return $ret; 68 | } 69 | 70 | /** 71 | * Uses bitwise operators instead of table-lookups to turn 8-bit integers 72 | * into 6-bit integers. 73 | * 74 | * @param int $src 75 | * @return string 76 | */ 77 | protected static function encode6Bits(int $src): string 78 | { 79 | $diff = 0x41; 80 | 81 | // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6 82 | $diff += ((25 - $src) >> 8) & 6; 83 | 84 | // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75 85 | $diff -= ((51 - $src) >> 8) & 75; 86 | 87 | // if ($src > 61) $diff += 0x2d - 0x30 - 10; // -13 88 | $diff -= ((61 - $src) >> 8) & 13; 89 | 90 | // if ($src > 62) $diff += 0x5f - 0x2b - 1; // 3 91 | $diff += ((62 - $src) >> 8) & 49; 92 | 93 | return \pack('C', $src + $diff); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Binary.php: -------------------------------------------------------------------------------- 1 | $chunk */ 53 | $chunk = \unpack('C', $binString[$i]); 54 | $c = $chunk[1] & 0xf; 55 | $b = $chunk[1] >> 4; 56 | 57 | $hex .= \pack( 58 | 'CC', 59 | (87 + $b + ((($b - 10) >> 8) & ~38)), 60 | (87 + $c + ((($c - 10) >> 8) & ~38)) 61 | ); 62 | } 63 | return $hex; 64 | } 65 | 66 | /** 67 | * Convert a binary string into a hexadecimal string without cache-timing 68 | * leaks, returning uppercase letters (as per RFC 4648) 69 | * 70 | * @param string $binString (raw binary) 71 | * @return string 72 | * @throws TypeError 73 | */ 74 | public static function encodeUpper( 75 | #[\SensitiveParameter] 76 | string $binString 77 | ): string { 78 | $hex = ''; 79 | $len = Binary::safeStrlen($binString); 80 | 81 | for ($i = 0; $i < $len; ++$i) { 82 | /** @var array $chunk */ 83 | $chunk = \unpack('C', $binString[$i]); 84 | $c = $chunk[1] & 0xf; 85 | $b = $chunk[1] >> 4; 86 | 87 | $hex .= \pack( 88 | 'CC', 89 | (55 + $b + ((($b - 10) >> 8) & ~6)), 90 | (55 + $c + ((($c - 10) >> 8) & ~6)) 91 | ); 92 | } 93 | return $hex; 94 | } 95 | 96 | /** 97 | * Convert a hexadecimal string into a binary string without cache-timing 98 | * leaks 99 | * 100 | * @param string $encodedString 101 | * @param bool $strictPadding 102 | * @return string (raw binary) 103 | * @throws RangeException 104 | */ 105 | public static function decode( 106 | #[\SensitiveParameter] 107 | string $encodedString, 108 | bool $strictPadding = false 109 | ): string { 110 | $hex_pos = 0; 111 | $bin = ''; 112 | $c_acc = 0; 113 | $hex_len = Binary::safeStrlen($encodedString); 114 | $state = 0; 115 | if (($hex_len & 1) !== 0) { 116 | if ($strictPadding) { 117 | throw new RangeException( 118 | 'Expected an even number of hexadecimal characters' 119 | ); 120 | } else { 121 | $encodedString = '0' . $encodedString; 122 | ++$hex_len; 123 | } 124 | } 125 | 126 | /** @var array $chunk */ 127 | $chunk = \unpack('C*', $encodedString); 128 | while ($hex_pos < $hex_len) { 129 | ++$hex_pos; 130 | $c = $chunk[$hex_pos]; 131 | $c_num = $c ^ 48; 132 | $c_num0 = ($c_num - 10) >> 8; 133 | $c_alpha = ($c & ~32) - 55; 134 | $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8; 135 | 136 | if (($c_num0 | $c_alpha0) === 0) { 137 | throw new RangeException( 138 | 'Expected hexadecimal character' 139 | ); 140 | } 141 | $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0); 142 | if ($state === 0) { 143 | $c_acc = $c_val * 16; 144 | } else { 145 | $bin .= \pack('C', $c_acc | $c_val); 146 | } 147 | $state ^= 1; 148 | } 149 | return $bin; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/RFC4648.php: -------------------------------------------------------------------------------- 1 | "Zm9v" 43 | * 44 | * @param string $str 45 | * @return string 46 | * 47 | * @throws TypeError 48 | */ 49 | public static function base64Encode( 50 | #[\SensitiveParameter] 51 | string $str 52 | ): string { 53 | return Base64::encode($str); 54 | } 55 | 56 | /** 57 | * RFC 4648 Base64 decoding 58 | * 59 | * "Zm9v" -> "foo" 60 | * 61 | * @param string $str 62 | * @return string 63 | * 64 | * @throws TypeError 65 | */ 66 | public static function base64Decode( 67 | #[\SensitiveParameter] 68 | string $str 69 | ): string { 70 | return Base64::decode($str, true); 71 | } 72 | 73 | /** 74 | * RFC 4648 Base64 (URL Safe) encoding 75 | * 76 | * "foo" -> "Zm9v" 77 | * 78 | * @param string $str 79 | * @return string 80 | * 81 | * @throws TypeError 82 | */ 83 | public static function base64UrlSafeEncode( 84 | #[\SensitiveParameter] 85 | string $str 86 | ): string { 87 | return Base64UrlSafe::encode($str); 88 | } 89 | 90 | /** 91 | * RFC 4648 Base64 (URL Safe) decoding 92 | * 93 | * "Zm9v" -> "foo" 94 | * 95 | * @param string $str 96 | * @return string 97 | * 98 | * @throws TypeError 99 | */ 100 | public static function base64UrlSafeDecode( 101 | #[\SensitiveParameter] 102 | string $str 103 | ): string { 104 | return Base64UrlSafe::decode($str, true); 105 | } 106 | 107 | /** 108 | * RFC 4648 Base32 encoding 109 | * 110 | * "foo" -> "MZXW6===" 111 | * 112 | * @param string $str 113 | * @return string 114 | * 115 | * @throws TypeError 116 | */ 117 | public static function base32Encode( 118 | #[\SensitiveParameter] 119 | string $str 120 | ): string { 121 | return Base32::encodeUpper($str); 122 | } 123 | 124 | /** 125 | * RFC 4648 Base32 encoding 126 | * 127 | * "MZXW6===" -> "foo" 128 | * 129 | * @param string $str 130 | * @return string 131 | * 132 | * @throws TypeError 133 | */ 134 | public static function base32Decode( 135 | #[\SensitiveParameter] 136 | string $str 137 | ): string { 138 | return Base32::decodeUpper($str, true); 139 | } 140 | 141 | /** 142 | * RFC 4648 Base32-Hex encoding 143 | * 144 | * "foo" -> "CPNMU===" 145 | * 146 | * @param string $str 147 | * @return string 148 | * 149 | * @throws TypeError 150 | */ 151 | public static function base32HexEncode( 152 | #[\SensitiveParameter] 153 | string $str 154 | ): string { 155 | return Base32::encodeUpper($str); 156 | } 157 | 158 | /** 159 | * RFC 4648 Base32-Hex decoding 160 | * 161 | * "CPNMU===" -> "foo" 162 | * 163 | * @param string $str 164 | * @return string 165 | * 166 | * @throws TypeError 167 | */ 168 | public static function base32HexDecode( 169 | #[\SensitiveParameter] 170 | string $str 171 | ): string { 172 | return Base32::decodeUpper($str, true); 173 | } 174 | 175 | /** 176 | * RFC 4648 Base16 decoding 177 | * 178 | * "foo" -> "666F6F" 179 | * 180 | * @param string $str 181 | * @return string 182 | * 183 | * @throws TypeError 184 | */ 185 | public static function base16Encode( 186 | #[\SensitiveParameter] 187 | string $str 188 | ): string { 189 | return Hex::encodeUpper($str); 190 | } 191 | 192 | /** 193 | * RFC 4648 Base16 decoding 194 | * 195 | * "666F6F" -> "foo" 196 | * 197 | * @param string $str 198 | * @return string 199 | */ 200 | public static function base16Decode( 201 | #[\SensitiveParameter] 202 | string $str 203 | ): string { 204 | return Hex::decode($str, true); 205 | } 206 | } 207 | --------------------------------------------------------------------------------