1927933f5SAndreas Gohr<?php 2927933f5SAndreas Gohrdeclare(strict_types=1); 3927933f5SAndreas Gohrnamespace ParagonIE\ConstantTime; 4927933f5SAndreas Gohr 5927933f5SAndreas Gohruse InvalidArgumentException; 6927933f5SAndreas Gohruse RangeException; 7927933f5SAndreas Gohruse TypeError; 8927933f5SAndreas Gohr 9927933f5SAndreas Gohr/** 10927933f5SAndreas Gohr * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. 11927933f5SAndreas Gohr * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) 12927933f5SAndreas Gohr * 13927933f5SAndreas Gohr * Permission is hereby granted, free of charge, to any person obtaining a copy 14927933f5SAndreas Gohr * of this software and associated documentation files (the "Software"), to deal 15927933f5SAndreas Gohr * in the Software without restriction, including without limitation the rights 16927933f5SAndreas Gohr * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17927933f5SAndreas Gohr * copies of the Software, and to permit persons to whom the Software is 18927933f5SAndreas Gohr * furnished to do so, subject to the following conditions: 19927933f5SAndreas Gohr * 20927933f5SAndreas Gohr * The above copyright notice and this permission notice shall be included in all 21927933f5SAndreas Gohr * copies or substantial portions of the Software. 22927933f5SAndreas Gohr * 23927933f5SAndreas Gohr * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24927933f5SAndreas Gohr * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25927933f5SAndreas Gohr * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26927933f5SAndreas Gohr * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27927933f5SAndreas Gohr * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28927933f5SAndreas Gohr * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29927933f5SAndreas Gohr * SOFTWARE. 30927933f5SAndreas Gohr */ 31927933f5SAndreas Gohr 32927933f5SAndreas Gohr/** 33927933f5SAndreas Gohr * Class Base32 34927933f5SAndreas Gohr * [A-Z][2-7] 35927933f5SAndreas Gohr * 36927933f5SAndreas Gohr * @package ParagonIE\ConstantTime 37927933f5SAndreas Gohr */ 38927933f5SAndreas Gohrabstract class Base32 implements EncoderInterface 39927933f5SAndreas Gohr{ 40927933f5SAndreas Gohr /** 41927933f5SAndreas Gohr * Decode a Base32-encoded string into raw binary 42927933f5SAndreas Gohr * 43927933f5SAndreas Gohr * @param string $encodedString 44927933f5SAndreas Gohr * @param bool $strictPadding 45927933f5SAndreas Gohr * @return string 46927933f5SAndreas Gohr */ 47*850e6620SAndreas Gohr public static function decode( 48*850e6620SAndreas Gohr #[\SensitiveParameter] 49*850e6620SAndreas Gohr string $encodedString, 50*850e6620SAndreas Gohr bool $strictPadding = false 51*850e6620SAndreas Gohr ): string { 52927933f5SAndreas Gohr return static::doDecode($encodedString, false, $strictPadding); 53927933f5SAndreas Gohr } 54927933f5SAndreas Gohr 55927933f5SAndreas Gohr /** 56927933f5SAndreas Gohr * Decode an uppercase Base32-encoded string into raw binary 57927933f5SAndreas Gohr * 58927933f5SAndreas Gohr * @param string $src 59927933f5SAndreas Gohr * @param bool $strictPadding 60927933f5SAndreas Gohr * @return string 61927933f5SAndreas Gohr */ 62*850e6620SAndreas Gohr public static function decodeUpper( 63*850e6620SAndreas Gohr #[\SensitiveParameter] 64*850e6620SAndreas Gohr string $src, 65*850e6620SAndreas Gohr bool $strictPadding = false 66*850e6620SAndreas Gohr ): string { 67927933f5SAndreas Gohr return static::doDecode($src, true, $strictPadding); 68927933f5SAndreas Gohr } 69927933f5SAndreas Gohr 70927933f5SAndreas Gohr /** 71927933f5SAndreas Gohr * Encode into Base32 (RFC 4648) 72927933f5SAndreas Gohr * 73927933f5SAndreas Gohr * @param string $binString 74927933f5SAndreas Gohr * @return string 75927933f5SAndreas Gohr * @throws TypeError 76927933f5SAndreas Gohr */ 77*850e6620SAndreas Gohr public static function encode( 78*850e6620SAndreas Gohr #[\SensitiveParameter] 79*850e6620SAndreas Gohr string $binString 80*850e6620SAndreas Gohr ): string { 81927933f5SAndreas Gohr return static::doEncode($binString, false, true); 82927933f5SAndreas Gohr } 83*850e6620SAndreas Gohr 84927933f5SAndreas Gohr /** 85927933f5SAndreas Gohr * Encode into Base32 (RFC 4648) 86927933f5SAndreas Gohr * 87927933f5SAndreas Gohr * @param string $src 88927933f5SAndreas Gohr * @return string 89927933f5SAndreas Gohr * @throws TypeError 90927933f5SAndreas Gohr */ 91*850e6620SAndreas Gohr public static function encodeUnpadded( 92*850e6620SAndreas Gohr #[\SensitiveParameter] 93*850e6620SAndreas Gohr string $src 94*850e6620SAndreas Gohr ): string { 95927933f5SAndreas Gohr return static::doEncode($src, false, false); 96927933f5SAndreas Gohr } 97927933f5SAndreas Gohr 98927933f5SAndreas Gohr /** 99927933f5SAndreas Gohr * Encode into uppercase Base32 (RFC 4648) 100927933f5SAndreas Gohr * 101927933f5SAndreas Gohr * @param string $src 102927933f5SAndreas Gohr * @return string 103927933f5SAndreas Gohr * @throws TypeError 104927933f5SAndreas Gohr */ 105*850e6620SAndreas Gohr public static function encodeUpper( 106*850e6620SAndreas Gohr #[\SensitiveParameter] 107*850e6620SAndreas Gohr string $src 108*850e6620SAndreas Gohr ): string { 109927933f5SAndreas Gohr return static::doEncode($src, true, true); 110927933f5SAndreas Gohr } 111927933f5SAndreas Gohr 112927933f5SAndreas Gohr /** 113927933f5SAndreas Gohr * Encode into uppercase Base32 (RFC 4648) 114927933f5SAndreas Gohr * 115927933f5SAndreas Gohr * @param string $src 116927933f5SAndreas Gohr * @return string 117927933f5SAndreas Gohr * @throws TypeError 118927933f5SAndreas Gohr */ 119*850e6620SAndreas Gohr public static function encodeUpperUnpadded( 120*850e6620SAndreas Gohr #[\SensitiveParameter] 121*850e6620SAndreas Gohr string $src 122*850e6620SAndreas Gohr ): string { 123927933f5SAndreas Gohr return static::doEncode($src, true, false); 124927933f5SAndreas Gohr } 125927933f5SAndreas Gohr 126927933f5SAndreas Gohr /** 127927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 5-bit integers 128927933f5SAndreas Gohr * into 8-bit integers. 129927933f5SAndreas Gohr * 130927933f5SAndreas Gohr * @param int $src 131927933f5SAndreas Gohr * @return int 132927933f5SAndreas Gohr */ 133927933f5SAndreas Gohr protected static function decode5Bits(int $src): int 134927933f5SAndreas Gohr { 135927933f5SAndreas Gohr $ret = -1; 136927933f5SAndreas Gohr 137927933f5SAndreas Gohr // if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64 138927933f5SAndreas Gohr $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96); 139927933f5SAndreas Gohr 140927933f5SAndreas Gohr // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 141927933f5SAndreas Gohr $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 142927933f5SAndreas Gohr 143927933f5SAndreas Gohr return $ret; 144927933f5SAndreas Gohr } 145927933f5SAndreas Gohr 146927933f5SAndreas Gohr /** 147927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 5-bit integers 148927933f5SAndreas Gohr * into 8-bit integers. 149927933f5SAndreas Gohr * 150927933f5SAndreas Gohr * Uppercase variant. 151927933f5SAndreas Gohr * 152927933f5SAndreas Gohr * @param int $src 153927933f5SAndreas Gohr * @return int 154927933f5SAndreas Gohr */ 155927933f5SAndreas Gohr protected static function decode5BitsUpper(int $src): int 156927933f5SAndreas Gohr { 157927933f5SAndreas Gohr $ret = -1; 158927933f5SAndreas Gohr 159927933f5SAndreas Gohr // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64 160927933f5SAndreas Gohr $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); 161927933f5SAndreas Gohr 162927933f5SAndreas Gohr // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 163927933f5SAndreas Gohr $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 164927933f5SAndreas Gohr 165927933f5SAndreas Gohr return $ret; 166927933f5SAndreas Gohr } 167927933f5SAndreas Gohr 168927933f5SAndreas Gohr /** 169927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 8-bit integers 170927933f5SAndreas Gohr * into 5-bit integers. 171927933f5SAndreas Gohr * 172927933f5SAndreas Gohr * @param int $src 173927933f5SAndreas Gohr * @return string 174927933f5SAndreas Gohr */ 175927933f5SAndreas Gohr protected static function encode5Bits(int $src): string 176927933f5SAndreas Gohr { 177927933f5SAndreas Gohr $diff = 0x61; 178927933f5SAndreas Gohr 179927933f5SAndreas Gohr // if ($src > 25) $ret -= 72; 180927933f5SAndreas Gohr $diff -= ((25 - $src) >> 8) & 73; 181927933f5SAndreas Gohr 182927933f5SAndreas Gohr return \pack('C', $src + $diff); 183927933f5SAndreas Gohr } 184927933f5SAndreas Gohr 185927933f5SAndreas Gohr /** 186927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 8-bit integers 187927933f5SAndreas Gohr * into 5-bit integers. 188927933f5SAndreas Gohr * 189927933f5SAndreas Gohr * Uppercase variant. 190927933f5SAndreas Gohr * 191927933f5SAndreas Gohr * @param int $src 192927933f5SAndreas Gohr * @return string 193927933f5SAndreas Gohr */ 194927933f5SAndreas Gohr protected static function encode5BitsUpper(int $src): string 195927933f5SAndreas Gohr { 196927933f5SAndreas Gohr $diff = 0x41; 197927933f5SAndreas Gohr 198927933f5SAndreas Gohr // if ($src > 25) $ret -= 40; 199927933f5SAndreas Gohr $diff -= ((25 - $src) >> 8) & 41; 200927933f5SAndreas Gohr 201927933f5SAndreas Gohr return \pack('C', $src + $diff); 202927933f5SAndreas Gohr } 203927933f5SAndreas Gohr 204927933f5SAndreas Gohr /** 205927933f5SAndreas Gohr * @param string $encodedString 206927933f5SAndreas Gohr * @param bool $upper 207927933f5SAndreas Gohr * @return string 208927933f5SAndreas Gohr */ 209*850e6620SAndreas Gohr public static function decodeNoPadding( 210*850e6620SAndreas Gohr #[\SensitiveParameter] 211*850e6620SAndreas Gohr string $encodedString, 212*850e6620SAndreas Gohr bool $upper = false 213*850e6620SAndreas Gohr ): string { 214927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($encodedString); 215927933f5SAndreas Gohr if ($srcLen === 0) { 216927933f5SAndreas Gohr return ''; 217927933f5SAndreas Gohr } 218927933f5SAndreas Gohr if (($srcLen & 7) === 0) { 219927933f5SAndreas Gohr for ($j = 0; $j < 7 && $j < $srcLen; ++$j) { 220927933f5SAndreas Gohr if ($encodedString[$srcLen - $j - 1] === '=') { 221927933f5SAndreas Gohr throw new InvalidArgumentException( 222927933f5SAndreas Gohr "decodeNoPadding() doesn't tolerate padding" 223927933f5SAndreas Gohr ); 224927933f5SAndreas Gohr } 225927933f5SAndreas Gohr } 226927933f5SAndreas Gohr } 227927933f5SAndreas Gohr return static::doDecode( 228927933f5SAndreas Gohr $encodedString, 229927933f5SAndreas Gohr $upper, 230927933f5SAndreas Gohr true 231927933f5SAndreas Gohr ); 232927933f5SAndreas Gohr } 233927933f5SAndreas Gohr 234927933f5SAndreas Gohr /** 235927933f5SAndreas Gohr * Base32 decoding 236927933f5SAndreas Gohr * 237927933f5SAndreas Gohr * @param string $src 238927933f5SAndreas Gohr * @param bool $upper 239927933f5SAndreas Gohr * @param bool $strictPadding 240927933f5SAndreas Gohr * @return string 241927933f5SAndreas Gohr * 242927933f5SAndreas Gohr * @throws TypeError 243927933f5SAndreas Gohr */ 244927933f5SAndreas Gohr protected static function doDecode( 245*850e6620SAndreas Gohr #[\SensitiveParameter] 246927933f5SAndreas Gohr string $src, 247927933f5SAndreas Gohr bool $upper = false, 248927933f5SAndreas Gohr bool $strictPadding = false 249927933f5SAndreas Gohr ): string { 250927933f5SAndreas Gohr // We do this to reduce code duplication: 251927933f5SAndreas Gohr $method = $upper 252927933f5SAndreas Gohr ? 'decode5BitsUpper' 253927933f5SAndreas Gohr : 'decode5Bits'; 254927933f5SAndreas Gohr 255927933f5SAndreas Gohr // Remove padding 256927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($src); 257927933f5SAndreas Gohr if ($srcLen === 0) { 258927933f5SAndreas Gohr return ''; 259927933f5SAndreas Gohr } 260927933f5SAndreas Gohr if ($strictPadding) { 261927933f5SAndreas Gohr if (($srcLen & 7) === 0) { 262927933f5SAndreas Gohr for ($j = 0; $j < 7; ++$j) { 263927933f5SAndreas Gohr if ($src[$srcLen - 1] === '=') { 264927933f5SAndreas Gohr $srcLen--; 265927933f5SAndreas Gohr } else { 266927933f5SAndreas Gohr break; 267927933f5SAndreas Gohr } 268927933f5SAndreas Gohr } 269927933f5SAndreas Gohr } 270927933f5SAndreas Gohr if (($srcLen & 7) === 1) { 271927933f5SAndreas Gohr throw new RangeException( 272927933f5SAndreas Gohr 'Incorrect padding' 273927933f5SAndreas Gohr ); 274927933f5SAndreas Gohr } 275927933f5SAndreas Gohr } else { 276927933f5SAndreas Gohr $src = \rtrim($src, '='); 277927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($src); 278927933f5SAndreas Gohr } 279927933f5SAndreas Gohr 280927933f5SAndreas Gohr $err = 0; 281927933f5SAndreas Gohr $dest = ''; 282927933f5SAndreas Gohr // Main loop (no padding): 283927933f5SAndreas Gohr for ($i = 0; $i + 8 <= $srcLen; $i += 8) { 284927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 285927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8)); 286927933f5SAndreas Gohr /** @var int $c0 */ 287927933f5SAndreas Gohr $c0 = static::$method($chunk[1]); 288927933f5SAndreas Gohr /** @var int $c1 */ 289927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 290927933f5SAndreas Gohr /** @var int $c2 */ 291927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 292927933f5SAndreas Gohr /** @var int $c3 */ 293927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 294927933f5SAndreas Gohr /** @var int $c4 */ 295927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 296927933f5SAndreas Gohr /** @var int $c5 */ 297927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 298927933f5SAndreas Gohr /** @var int $c6 */ 299927933f5SAndreas Gohr $c6 = static::$method($chunk[7]); 300927933f5SAndreas Gohr /** @var int $c7 */ 301927933f5SAndreas Gohr $c7 = static::$method($chunk[8]); 302927933f5SAndreas Gohr 303927933f5SAndreas Gohr $dest .= \pack( 304927933f5SAndreas Gohr 'CCCCC', 305927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 306927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 307927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 308927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff, 309927933f5SAndreas Gohr (($c6 << 5) | ($c7 ) ) & 0xff 310927933f5SAndreas Gohr ); 311927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8; 312927933f5SAndreas Gohr } 313927933f5SAndreas Gohr // The last chunk, which may have padding: 314927933f5SAndreas Gohr if ($i < $srcLen) { 315927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 316927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); 317927933f5SAndreas Gohr /** @var int $c0 */ 318927933f5SAndreas Gohr $c0 = static::$method($chunk[1]); 319927933f5SAndreas Gohr 320927933f5SAndreas Gohr if ($i + 6 < $srcLen) { 321927933f5SAndreas Gohr /** @var int $c1 */ 322927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 323927933f5SAndreas Gohr /** @var int $c2 */ 324927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 325927933f5SAndreas Gohr /** @var int $c3 */ 326927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 327927933f5SAndreas Gohr /** @var int $c4 */ 328927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 329927933f5SAndreas Gohr /** @var int $c5 */ 330927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 331927933f5SAndreas Gohr /** @var int $c6 */ 332927933f5SAndreas Gohr $c6 = static::$method($chunk[7]); 333927933f5SAndreas Gohr 334927933f5SAndreas Gohr $dest .= \pack( 335927933f5SAndreas Gohr 'CCCC', 336927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 337927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 338927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 339927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff 340927933f5SAndreas Gohr ); 341927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8; 342927933f5SAndreas Gohr if ($strictPadding) { 343927933f5SAndreas Gohr $err |= ($c6 << 5) & 0xff; 344927933f5SAndreas Gohr } 345927933f5SAndreas Gohr } elseif ($i + 5 < $srcLen) { 346927933f5SAndreas Gohr /** @var int $c1 */ 347927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 348927933f5SAndreas Gohr /** @var int $c2 */ 349927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 350927933f5SAndreas Gohr /** @var int $c3 */ 351927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 352927933f5SAndreas Gohr /** @var int $c4 */ 353927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 354927933f5SAndreas Gohr /** @var int $c5 */ 355927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 356927933f5SAndreas Gohr 357927933f5SAndreas Gohr $dest .= \pack( 358927933f5SAndreas Gohr 'CCCC', 359927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 360927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 361927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 362927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) ) & 0xff 363927933f5SAndreas Gohr ); 364927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8; 365927933f5SAndreas Gohr } elseif ($i + 4 < $srcLen) { 366927933f5SAndreas Gohr /** @var int $c1 */ 367927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 368927933f5SAndreas Gohr /** @var int $c2 */ 369927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 370927933f5SAndreas Gohr /** @var int $c3 */ 371927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 372927933f5SAndreas Gohr /** @var int $c4 */ 373927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 374927933f5SAndreas Gohr 375927933f5SAndreas Gohr $dest .= \pack( 376927933f5SAndreas Gohr 'CCC', 377927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 378927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 379927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff 380927933f5SAndreas Gohr ); 381927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8; 382927933f5SAndreas Gohr if ($strictPadding) { 383927933f5SAndreas Gohr $err |= ($c4 << 7) & 0xff; 384927933f5SAndreas Gohr } 385927933f5SAndreas Gohr } elseif ($i + 3 < $srcLen) { 386927933f5SAndreas Gohr /** @var int $c1 */ 387927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 388927933f5SAndreas Gohr /** @var int $c2 */ 389927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 390927933f5SAndreas Gohr /** @var int $c3 */ 391927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 392927933f5SAndreas Gohr 393927933f5SAndreas Gohr $dest .= \pack( 394927933f5SAndreas Gohr 'CC', 395927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 396927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff 397927933f5SAndreas Gohr ); 398927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3) >> 8; 399927933f5SAndreas Gohr if ($strictPadding) { 400927933f5SAndreas Gohr $err |= ($c3 << 4) & 0xff; 401927933f5SAndreas Gohr } 402927933f5SAndreas Gohr } elseif ($i + 2 < $srcLen) { 403927933f5SAndreas Gohr /** @var int $c1 */ 404927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 405927933f5SAndreas Gohr /** @var int $c2 */ 406927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 407927933f5SAndreas Gohr 408927933f5SAndreas Gohr $dest .= \pack( 409927933f5SAndreas Gohr 'CC', 410927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 411927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) ) & 0xff 412927933f5SAndreas Gohr ); 413927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2) >> 8; 414927933f5SAndreas Gohr if ($strictPadding) { 415927933f5SAndreas Gohr $err |= ($c2 << 6) & 0xff; 416927933f5SAndreas Gohr } 417927933f5SAndreas Gohr } elseif ($i + 1 < $srcLen) { 418927933f5SAndreas Gohr /** @var int $c1 */ 419927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 420927933f5SAndreas Gohr 421927933f5SAndreas Gohr $dest .= \pack( 422927933f5SAndreas Gohr 'C', 423927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff 424927933f5SAndreas Gohr ); 425927933f5SAndreas Gohr $err |= ($c0 | $c1) >> 8; 426927933f5SAndreas Gohr if ($strictPadding) { 427927933f5SAndreas Gohr $err |= ($c1 << 6) & 0xff; 428927933f5SAndreas Gohr } 429927933f5SAndreas Gohr } else { 430927933f5SAndreas Gohr $dest .= \pack( 431927933f5SAndreas Gohr 'C', 432927933f5SAndreas Gohr (($c0 << 3) ) & 0xff 433927933f5SAndreas Gohr ); 434927933f5SAndreas Gohr $err |= ($c0) >> 8; 435927933f5SAndreas Gohr } 436927933f5SAndreas Gohr } 437927933f5SAndreas Gohr $check = ($err === 0); 438927933f5SAndreas Gohr if (!$check) { 439927933f5SAndreas Gohr throw new RangeException( 440927933f5SAndreas Gohr 'Base32::doDecode() only expects characters in the correct base32 alphabet' 441927933f5SAndreas Gohr ); 442927933f5SAndreas Gohr } 443927933f5SAndreas Gohr return $dest; 444927933f5SAndreas Gohr } 445927933f5SAndreas Gohr 446927933f5SAndreas Gohr /** 447927933f5SAndreas Gohr * Base32 Encoding 448927933f5SAndreas Gohr * 449927933f5SAndreas Gohr * @param string $src 450927933f5SAndreas Gohr * @param bool $upper 451927933f5SAndreas Gohr * @param bool $pad 452927933f5SAndreas Gohr * @return string 453927933f5SAndreas Gohr * @throws TypeError 454927933f5SAndreas Gohr */ 455*850e6620SAndreas Gohr protected static function doEncode( 456*850e6620SAndreas Gohr #[\SensitiveParameter] 457*850e6620SAndreas Gohr string $src, 458*850e6620SAndreas Gohr bool $upper = false, 459*850e6620SAndreas Gohr $pad = true 460*850e6620SAndreas Gohr ): string { 461927933f5SAndreas Gohr // We do this to reduce code duplication: 462927933f5SAndreas Gohr $method = $upper 463927933f5SAndreas Gohr ? 'encode5BitsUpper' 464927933f5SAndreas Gohr : 'encode5Bits'; 465927933f5SAndreas Gohr 466927933f5SAndreas Gohr $dest = ''; 467927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($src); 468927933f5SAndreas Gohr 469927933f5SAndreas Gohr // Main loop (no padding): 470927933f5SAndreas Gohr for ($i = 0; $i + 5 <= $srcLen; $i += 5) { 471927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 472927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5)); 473927933f5SAndreas Gohr $b0 = $chunk[1]; 474927933f5SAndreas Gohr $b1 = $chunk[2]; 475927933f5SAndreas Gohr $b2 = $chunk[3]; 476927933f5SAndreas Gohr $b3 = $chunk[4]; 477927933f5SAndreas Gohr $b4 = $chunk[5]; 478927933f5SAndreas Gohr $dest .= 479927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 480927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 481927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 482927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 483927933f5SAndreas Gohr static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 484927933f5SAndreas Gohr static::$method((($b3 >> 2) ) & 31) . 485927933f5SAndreas Gohr static::$method((($b3 << 3) | ($b4 >> 5)) & 31) . 486927933f5SAndreas Gohr static::$method( $b4 & 31); 487927933f5SAndreas Gohr } 488927933f5SAndreas Gohr // The last chunk, which may have padding: 489927933f5SAndreas Gohr if ($i < $srcLen) { 490927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 491927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); 492927933f5SAndreas Gohr $b0 = $chunk[1]; 493927933f5SAndreas Gohr if ($i + 3 < $srcLen) { 494927933f5SAndreas Gohr $b1 = $chunk[2]; 495927933f5SAndreas Gohr $b2 = $chunk[3]; 496927933f5SAndreas Gohr $b3 = $chunk[4]; 497927933f5SAndreas Gohr $dest .= 498927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 499927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 500927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 501927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 502927933f5SAndreas Gohr static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 503927933f5SAndreas Gohr static::$method((($b3 >> 2) ) & 31) . 504927933f5SAndreas Gohr static::$method((($b3 << 3) ) & 31); 505927933f5SAndreas Gohr if ($pad) { 506927933f5SAndreas Gohr $dest .= '='; 507927933f5SAndreas Gohr } 508927933f5SAndreas Gohr } elseif ($i + 2 < $srcLen) { 509927933f5SAndreas Gohr $b1 = $chunk[2]; 510927933f5SAndreas Gohr $b2 = $chunk[3]; 511927933f5SAndreas Gohr $dest .= 512927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 513927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 514927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 515927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 516927933f5SAndreas Gohr static::$method((($b2 << 1) ) & 31); 517927933f5SAndreas Gohr if ($pad) { 518927933f5SAndreas Gohr $dest .= '==='; 519927933f5SAndreas Gohr } 520927933f5SAndreas Gohr } elseif ($i + 1 < $srcLen) { 521927933f5SAndreas Gohr $b1 = $chunk[2]; 522927933f5SAndreas Gohr $dest .= 523927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 524927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 525927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 526927933f5SAndreas Gohr static::$method((($b1 << 4) ) & 31); 527927933f5SAndreas Gohr if ($pad) { 528927933f5SAndreas Gohr $dest .= '===='; 529927933f5SAndreas Gohr } 530927933f5SAndreas Gohr } else { 531927933f5SAndreas Gohr $dest .= 532927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 533927933f5SAndreas Gohr static::$method( ($b0 << 2) & 31); 534927933f5SAndreas Gohr if ($pad) { 535927933f5SAndreas Gohr $dest .= '======'; 536927933f5SAndreas Gohr } 537927933f5SAndreas Gohr } 538927933f5SAndreas Gohr } 539927933f5SAndreas Gohr return $dest; 540927933f5SAndreas Gohr } 541927933f5SAndreas Gohr} 542