1*927933f5SAndreas Gohr<?php 2*927933f5SAndreas Gohrdeclare(strict_types=1); 3*927933f5SAndreas Gohrnamespace ParagonIE\ConstantTime; 4*927933f5SAndreas Gohr 5*927933f5SAndreas Gohruse InvalidArgumentException; 6*927933f5SAndreas Gohruse RangeException; 7*927933f5SAndreas Gohruse TypeError; 8*927933f5SAndreas Gohr 9*927933f5SAndreas Gohr/** 10*927933f5SAndreas Gohr * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. 11*927933f5SAndreas Gohr * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) 12*927933f5SAndreas Gohr * 13*927933f5SAndreas Gohr * Permission is hereby granted, free of charge, to any person obtaining a copy 14*927933f5SAndreas Gohr * of this software and associated documentation files (the "Software"), to deal 15*927933f5SAndreas Gohr * in the Software without restriction, including without limitation the rights 16*927933f5SAndreas Gohr * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17*927933f5SAndreas Gohr * copies of the Software, and to permit persons to whom the Software is 18*927933f5SAndreas Gohr * furnished to do so, subject to the following conditions: 19*927933f5SAndreas Gohr * 20*927933f5SAndreas Gohr * The above copyright notice and this permission notice shall be included in all 21*927933f5SAndreas Gohr * copies or substantial portions of the Software. 22*927933f5SAndreas Gohr * 23*927933f5SAndreas Gohr * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24*927933f5SAndreas Gohr * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25*927933f5SAndreas Gohr * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26*927933f5SAndreas Gohr * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27*927933f5SAndreas Gohr * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28*927933f5SAndreas Gohr * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29*927933f5SAndreas Gohr * SOFTWARE. 30*927933f5SAndreas Gohr */ 31*927933f5SAndreas Gohr 32*927933f5SAndreas Gohr/** 33*927933f5SAndreas Gohr * Class Base32 34*927933f5SAndreas Gohr * [A-Z][2-7] 35*927933f5SAndreas Gohr * 36*927933f5SAndreas Gohr * @package ParagonIE\ConstantTime 37*927933f5SAndreas Gohr */ 38*927933f5SAndreas Gohrabstract class Base32 implements EncoderInterface 39*927933f5SAndreas Gohr{ 40*927933f5SAndreas Gohr /** 41*927933f5SAndreas Gohr * Decode a Base32-encoded string into raw binary 42*927933f5SAndreas Gohr * 43*927933f5SAndreas Gohr * @param string $encodedString 44*927933f5SAndreas Gohr * @param bool $strictPadding 45*927933f5SAndreas Gohr * @return string 46*927933f5SAndreas Gohr */ 47*927933f5SAndreas Gohr public static function decode(string $encodedString, bool $strictPadding = false): string 48*927933f5SAndreas Gohr { 49*927933f5SAndreas Gohr return static::doDecode($encodedString, false, $strictPadding); 50*927933f5SAndreas Gohr } 51*927933f5SAndreas Gohr 52*927933f5SAndreas Gohr /** 53*927933f5SAndreas Gohr * Decode an uppercase Base32-encoded string into raw binary 54*927933f5SAndreas Gohr * 55*927933f5SAndreas Gohr * @param string $src 56*927933f5SAndreas Gohr * @param bool $strictPadding 57*927933f5SAndreas Gohr * @return string 58*927933f5SAndreas Gohr */ 59*927933f5SAndreas Gohr public static function decodeUpper(string $src, bool $strictPadding = false): string 60*927933f5SAndreas Gohr { 61*927933f5SAndreas Gohr return static::doDecode($src, true, $strictPadding); 62*927933f5SAndreas Gohr } 63*927933f5SAndreas Gohr 64*927933f5SAndreas Gohr /** 65*927933f5SAndreas Gohr * Encode into Base32 (RFC 4648) 66*927933f5SAndreas Gohr * 67*927933f5SAndreas Gohr * @param string $binString 68*927933f5SAndreas Gohr * @return string 69*927933f5SAndreas Gohr * @throws TypeError 70*927933f5SAndreas Gohr */ 71*927933f5SAndreas Gohr public static function encode(string $binString): string 72*927933f5SAndreas Gohr { 73*927933f5SAndreas Gohr return static::doEncode($binString, false, true); 74*927933f5SAndreas Gohr } 75*927933f5SAndreas Gohr /** 76*927933f5SAndreas Gohr * Encode into Base32 (RFC 4648) 77*927933f5SAndreas Gohr * 78*927933f5SAndreas Gohr * @param string $src 79*927933f5SAndreas Gohr * @return string 80*927933f5SAndreas Gohr * @throws TypeError 81*927933f5SAndreas Gohr */ 82*927933f5SAndreas Gohr public static function encodeUnpadded(string $src): string 83*927933f5SAndreas Gohr { 84*927933f5SAndreas Gohr return static::doEncode($src, false, false); 85*927933f5SAndreas Gohr } 86*927933f5SAndreas Gohr 87*927933f5SAndreas Gohr /** 88*927933f5SAndreas Gohr * Encode into uppercase Base32 (RFC 4648) 89*927933f5SAndreas Gohr * 90*927933f5SAndreas Gohr * @param string $src 91*927933f5SAndreas Gohr * @return string 92*927933f5SAndreas Gohr * @throws TypeError 93*927933f5SAndreas Gohr */ 94*927933f5SAndreas Gohr public static function encodeUpper(string $src): string 95*927933f5SAndreas Gohr { 96*927933f5SAndreas Gohr return static::doEncode($src, true, true); 97*927933f5SAndreas Gohr } 98*927933f5SAndreas Gohr 99*927933f5SAndreas Gohr /** 100*927933f5SAndreas Gohr * Encode into uppercase Base32 (RFC 4648) 101*927933f5SAndreas Gohr * 102*927933f5SAndreas Gohr * @param string $src 103*927933f5SAndreas Gohr * @return string 104*927933f5SAndreas Gohr * @throws TypeError 105*927933f5SAndreas Gohr */ 106*927933f5SAndreas Gohr public static function encodeUpperUnpadded(string $src): string 107*927933f5SAndreas Gohr { 108*927933f5SAndreas Gohr return static::doEncode($src, true, false); 109*927933f5SAndreas Gohr } 110*927933f5SAndreas Gohr 111*927933f5SAndreas Gohr /** 112*927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 5-bit integers 113*927933f5SAndreas Gohr * into 8-bit integers. 114*927933f5SAndreas Gohr * 115*927933f5SAndreas Gohr * @param int $src 116*927933f5SAndreas Gohr * @return int 117*927933f5SAndreas Gohr */ 118*927933f5SAndreas Gohr protected static function decode5Bits(int $src): int 119*927933f5SAndreas Gohr { 120*927933f5SAndreas Gohr $ret = -1; 121*927933f5SAndreas Gohr 122*927933f5SAndreas Gohr // if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64 123*927933f5SAndreas Gohr $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96); 124*927933f5SAndreas Gohr 125*927933f5SAndreas Gohr // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 126*927933f5SAndreas Gohr $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 127*927933f5SAndreas Gohr 128*927933f5SAndreas Gohr return $ret; 129*927933f5SAndreas Gohr } 130*927933f5SAndreas Gohr 131*927933f5SAndreas Gohr /** 132*927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 5-bit integers 133*927933f5SAndreas Gohr * into 8-bit integers. 134*927933f5SAndreas Gohr * 135*927933f5SAndreas Gohr * Uppercase variant. 136*927933f5SAndreas Gohr * 137*927933f5SAndreas Gohr * @param int $src 138*927933f5SAndreas Gohr * @return int 139*927933f5SAndreas Gohr */ 140*927933f5SAndreas Gohr protected static function decode5BitsUpper(int $src): int 141*927933f5SAndreas Gohr { 142*927933f5SAndreas Gohr $ret = -1; 143*927933f5SAndreas Gohr 144*927933f5SAndreas Gohr // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64 145*927933f5SAndreas Gohr $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); 146*927933f5SAndreas Gohr 147*927933f5SAndreas Gohr // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 148*927933f5SAndreas Gohr $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 149*927933f5SAndreas Gohr 150*927933f5SAndreas Gohr return $ret; 151*927933f5SAndreas Gohr } 152*927933f5SAndreas Gohr 153*927933f5SAndreas Gohr /** 154*927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 8-bit integers 155*927933f5SAndreas Gohr * into 5-bit integers. 156*927933f5SAndreas Gohr * 157*927933f5SAndreas Gohr * @param int $src 158*927933f5SAndreas Gohr * @return string 159*927933f5SAndreas Gohr */ 160*927933f5SAndreas Gohr protected static function encode5Bits(int $src): string 161*927933f5SAndreas Gohr { 162*927933f5SAndreas Gohr $diff = 0x61; 163*927933f5SAndreas Gohr 164*927933f5SAndreas Gohr // if ($src > 25) $ret -= 72; 165*927933f5SAndreas Gohr $diff -= ((25 - $src) >> 8) & 73; 166*927933f5SAndreas Gohr 167*927933f5SAndreas Gohr return \pack('C', $src + $diff); 168*927933f5SAndreas Gohr } 169*927933f5SAndreas Gohr 170*927933f5SAndreas Gohr /** 171*927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 8-bit integers 172*927933f5SAndreas Gohr * into 5-bit integers. 173*927933f5SAndreas Gohr * 174*927933f5SAndreas Gohr * Uppercase variant. 175*927933f5SAndreas Gohr * 176*927933f5SAndreas Gohr * @param int $src 177*927933f5SAndreas Gohr * @return string 178*927933f5SAndreas Gohr */ 179*927933f5SAndreas Gohr protected static function encode5BitsUpper(int $src): string 180*927933f5SAndreas Gohr { 181*927933f5SAndreas Gohr $diff = 0x41; 182*927933f5SAndreas Gohr 183*927933f5SAndreas Gohr // if ($src > 25) $ret -= 40; 184*927933f5SAndreas Gohr $diff -= ((25 - $src) >> 8) & 41; 185*927933f5SAndreas Gohr 186*927933f5SAndreas Gohr return \pack('C', $src + $diff); 187*927933f5SAndreas Gohr } 188*927933f5SAndreas Gohr 189*927933f5SAndreas Gohr /** 190*927933f5SAndreas Gohr * @param string $encodedString 191*927933f5SAndreas Gohr * @param bool $upper 192*927933f5SAndreas Gohr * @return string 193*927933f5SAndreas Gohr */ 194*927933f5SAndreas Gohr public static function decodeNoPadding(string $encodedString, bool $upper = false): string 195*927933f5SAndreas Gohr { 196*927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($encodedString); 197*927933f5SAndreas Gohr if ($srcLen === 0) { 198*927933f5SAndreas Gohr return ''; 199*927933f5SAndreas Gohr } 200*927933f5SAndreas Gohr if (($srcLen & 7) === 0) { 201*927933f5SAndreas Gohr for ($j = 0; $j < 7 && $j < $srcLen; ++$j) { 202*927933f5SAndreas Gohr if ($encodedString[$srcLen - $j - 1] === '=') { 203*927933f5SAndreas Gohr throw new InvalidArgumentException( 204*927933f5SAndreas Gohr "decodeNoPadding() doesn't tolerate padding" 205*927933f5SAndreas Gohr ); 206*927933f5SAndreas Gohr } 207*927933f5SAndreas Gohr } 208*927933f5SAndreas Gohr } 209*927933f5SAndreas Gohr return static::doDecode( 210*927933f5SAndreas Gohr $encodedString, 211*927933f5SAndreas Gohr $upper, 212*927933f5SAndreas Gohr true 213*927933f5SAndreas Gohr ); 214*927933f5SAndreas Gohr } 215*927933f5SAndreas Gohr 216*927933f5SAndreas Gohr /** 217*927933f5SAndreas Gohr * Base32 decoding 218*927933f5SAndreas Gohr * 219*927933f5SAndreas Gohr * @param string $src 220*927933f5SAndreas Gohr * @param bool $upper 221*927933f5SAndreas Gohr * @param bool $strictPadding 222*927933f5SAndreas Gohr * @return string 223*927933f5SAndreas Gohr * 224*927933f5SAndreas Gohr * @throws TypeError 225*927933f5SAndreas Gohr * @psalm-suppress RedundantCondition 226*927933f5SAndreas Gohr */ 227*927933f5SAndreas Gohr protected static function doDecode( 228*927933f5SAndreas Gohr string $src, 229*927933f5SAndreas Gohr bool $upper = false, 230*927933f5SAndreas Gohr bool $strictPadding = false 231*927933f5SAndreas Gohr ): string { 232*927933f5SAndreas Gohr // We do this to reduce code duplication: 233*927933f5SAndreas Gohr $method = $upper 234*927933f5SAndreas Gohr ? 'decode5BitsUpper' 235*927933f5SAndreas Gohr : 'decode5Bits'; 236*927933f5SAndreas Gohr 237*927933f5SAndreas Gohr // Remove padding 238*927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($src); 239*927933f5SAndreas Gohr if ($srcLen === 0) { 240*927933f5SAndreas Gohr return ''; 241*927933f5SAndreas Gohr } 242*927933f5SAndreas Gohr if ($strictPadding) { 243*927933f5SAndreas Gohr if (($srcLen & 7) === 0) { 244*927933f5SAndreas Gohr for ($j = 0; $j < 7; ++$j) { 245*927933f5SAndreas Gohr if ($src[$srcLen - 1] === '=') { 246*927933f5SAndreas Gohr $srcLen--; 247*927933f5SAndreas Gohr } else { 248*927933f5SAndreas Gohr break; 249*927933f5SAndreas Gohr } 250*927933f5SAndreas Gohr } 251*927933f5SAndreas Gohr } 252*927933f5SAndreas Gohr if (($srcLen & 7) === 1) { 253*927933f5SAndreas Gohr throw new RangeException( 254*927933f5SAndreas Gohr 'Incorrect padding' 255*927933f5SAndreas Gohr ); 256*927933f5SAndreas Gohr } 257*927933f5SAndreas Gohr } else { 258*927933f5SAndreas Gohr $src = \rtrim($src, '='); 259*927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($src); 260*927933f5SAndreas Gohr } 261*927933f5SAndreas Gohr 262*927933f5SAndreas Gohr $err = 0; 263*927933f5SAndreas Gohr $dest = ''; 264*927933f5SAndreas Gohr // Main loop (no padding): 265*927933f5SAndreas Gohr for ($i = 0; $i + 8 <= $srcLen; $i += 8) { 266*927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 267*927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 8)); 268*927933f5SAndreas Gohr /** @var int $c0 */ 269*927933f5SAndreas Gohr $c0 = static::$method($chunk[1]); 270*927933f5SAndreas Gohr /** @var int $c1 */ 271*927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 272*927933f5SAndreas Gohr /** @var int $c2 */ 273*927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 274*927933f5SAndreas Gohr /** @var int $c3 */ 275*927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 276*927933f5SAndreas Gohr /** @var int $c4 */ 277*927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 278*927933f5SAndreas Gohr /** @var int $c5 */ 279*927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 280*927933f5SAndreas Gohr /** @var int $c6 */ 281*927933f5SAndreas Gohr $c6 = static::$method($chunk[7]); 282*927933f5SAndreas Gohr /** @var int $c7 */ 283*927933f5SAndreas Gohr $c7 = static::$method($chunk[8]); 284*927933f5SAndreas Gohr 285*927933f5SAndreas Gohr $dest .= \pack( 286*927933f5SAndreas Gohr 'CCCCC', 287*927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 288*927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 289*927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 290*927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff, 291*927933f5SAndreas Gohr (($c6 << 5) | ($c7 ) ) & 0xff 292*927933f5SAndreas Gohr ); 293*927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8; 294*927933f5SAndreas Gohr } 295*927933f5SAndreas Gohr // The last chunk, which may have padding: 296*927933f5SAndreas Gohr if ($i < $srcLen) { 297*927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 298*927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); 299*927933f5SAndreas Gohr /** @var int $c0 */ 300*927933f5SAndreas Gohr $c0 = static::$method($chunk[1]); 301*927933f5SAndreas Gohr 302*927933f5SAndreas Gohr if ($i + 6 < $srcLen) { 303*927933f5SAndreas Gohr /** @var int $c1 */ 304*927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 305*927933f5SAndreas Gohr /** @var int $c2 */ 306*927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 307*927933f5SAndreas Gohr /** @var int $c3 */ 308*927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 309*927933f5SAndreas Gohr /** @var int $c4 */ 310*927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 311*927933f5SAndreas Gohr /** @var int $c5 */ 312*927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 313*927933f5SAndreas Gohr /** @var int $c6 */ 314*927933f5SAndreas Gohr $c6 = static::$method($chunk[7]); 315*927933f5SAndreas Gohr 316*927933f5SAndreas Gohr $dest .= \pack( 317*927933f5SAndreas Gohr 'CCCC', 318*927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 319*927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 320*927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 321*927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff 322*927933f5SAndreas Gohr ); 323*927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8; 324*927933f5SAndreas Gohr if ($strictPadding) { 325*927933f5SAndreas Gohr $err |= ($c6 << 5) & 0xff; 326*927933f5SAndreas Gohr } 327*927933f5SAndreas Gohr } elseif ($i + 5 < $srcLen) { 328*927933f5SAndreas Gohr /** @var int $c1 */ 329*927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 330*927933f5SAndreas Gohr /** @var int $c2 */ 331*927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 332*927933f5SAndreas Gohr /** @var int $c3 */ 333*927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 334*927933f5SAndreas Gohr /** @var int $c4 */ 335*927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 336*927933f5SAndreas Gohr /** @var int $c5 */ 337*927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 338*927933f5SAndreas Gohr 339*927933f5SAndreas Gohr $dest .= \pack( 340*927933f5SAndreas Gohr 'CCCC', 341*927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 342*927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 343*927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 344*927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) ) & 0xff 345*927933f5SAndreas Gohr ); 346*927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8; 347*927933f5SAndreas Gohr } elseif ($i + 4 < $srcLen) { 348*927933f5SAndreas Gohr /** @var int $c1 */ 349*927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 350*927933f5SAndreas Gohr /** @var int $c2 */ 351*927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 352*927933f5SAndreas Gohr /** @var int $c3 */ 353*927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 354*927933f5SAndreas Gohr /** @var int $c4 */ 355*927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 356*927933f5SAndreas Gohr 357*927933f5SAndreas Gohr $dest .= \pack( 358*927933f5SAndreas Gohr 'CCC', 359*927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 360*927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 361*927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff 362*927933f5SAndreas Gohr ); 363*927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8; 364*927933f5SAndreas Gohr if ($strictPadding) { 365*927933f5SAndreas Gohr $err |= ($c4 << 7) & 0xff; 366*927933f5SAndreas Gohr } 367*927933f5SAndreas Gohr } elseif ($i + 3 < $srcLen) { 368*927933f5SAndreas Gohr /** @var int $c1 */ 369*927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 370*927933f5SAndreas Gohr /** @var int $c2 */ 371*927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 372*927933f5SAndreas Gohr /** @var int $c3 */ 373*927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 374*927933f5SAndreas Gohr 375*927933f5SAndreas Gohr $dest .= \pack( 376*927933f5SAndreas Gohr 'CC', 377*927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 378*927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff 379*927933f5SAndreas Gohr ); 380*927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3) >> 8; 381*927933f5SAndreas Gohr if ($strictPadding) { 382*927933f5SAndreas Gohr $err |= ($c3 << 4) & 0xff; 383*927933f5SAndreas Gohr } 384*927933f5SAndreas Gohr } elseif ($i + 2 < $srcLen) { 385*927933f5SAndreas Gohr /** @var int $c1 */ 386*927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 387*927933f5SAndreas Gohr /** @var int $c2 */ 388*927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 389*927933f5SAndreas Gohr 390*927933f5SAndreas Gohr $dest .= \pack( 391*927933f5SAndreas Gohr 'CC', 392*927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 393*927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) ) & 0xff 394*927933f5SAndreas Gohr ); 395*927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2) >> 8; 396*927933f5SAndreas Gohr if ($strictPadding) { 397*927933f5SAndreas Gohr $err |= ($c2 << 6) & 0xff; 398*927933f5SAndreas Gohr } 399*927933f5SAndreas Gohr } elseif ($i + 1 < $srcLen) { 400*927933f5SAndreas Gohr /** @var int $c1 */ 401*927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 402*927933f5SAndreas Gohr 403*927933f5SAndreas Gohr $dest .= \pack( 404*927933f5SAndreas Gohr 'C', 405*927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff 406*927933f5SAndreas Gohr ); 407*927933f5SAndreas Gohr $err |= ($c0 | $c1) >> 8; 408*927933f5SAndreas Gohr if ($strictPadding) { 409*927933f5SAndreas Gohr $err |= ($c1 << 6) & 0xff; 410*927933f5SAndreas Gohr } 411*927933f5SAndreas Gohr } else { 412*927933f5SAndreas Gohr $dest .= \pack( 413*927933f5SAndreas Gohr 'C', 414*927933f5SAndreas Gohr (($c0 << 3) ) & 0xff 415*927933f5SAndreas Gohr ); 416*927933f5SAndreas Gohr $err |= ($c0) >> 8; 417*927933f5SAndreas Gohr } 418*927933f5SAndreas Gohr } 419*927933f5SAndreas Gohr $check = ($err === 0); 420*927933f5SAndreas Gohr if (!$check) { 421*927933f5SAndreas Gohr throw new RangeException( 422*927933f5SAndreas Gohr 'Base32::doDecode() only expects characters in the correct base32 alphabet' 423*927933f5SAndreas Gohr ); 424*927933f5SAndreas Gohr } 425*927933f5SAndreas Gohr return $dest; 426*927933f5SAndreas Gohr } 427*927933f5SAndreas Gohr 428*927933f5SAndreas Gohr /** 429*927933f5SAndreas Gohr * Base32 Encoding 430*927933f5SAndreas Gohr * 431*927933f5SAndreas Gohr * @param string $src 432*927933f5SAndreas Gohr * @param bool $upper 433*927933f5SAndreas Gohr * @param bool $pad 434*927933f5SAndreas Gohr * @return string 435*927933f5SAndreas Gohr * @throws TypeError 436*927933f5SAndreas Gohr */ 437*927933f5SAndreas Gohr protected static function doEncode(string $src, bool $upper = false, $pad = true): string 438*927933f5SAndreas Gohr { 439*927933f5SAndreas Gohr // We do this to reduce code duplication: 440*927933f5SAndreas Gohr $method = $upper 441*927933f5SAndreas Gohr ? 'encode5BitsUpper' 442*927933f5SAndreas Gohr : 'encode5Bits'; 443*927933f5SAndreas Gohr 444*927933f5SAndreas Gohr $dest = ''; 445*927933f5SAndreas Gohr $srcLen = Binary::safeStrlen($src); 446*927933f5SAndreas Gohr 447*927933f5SAndreas Gohr // Main loop (no padding): 448*927933f5SAndreas Gohr for ($i = 0; $i + 5 <= $srcLen; $i += 5) { 449*927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 450*927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 5)); 451*927933f5SAndreas Gohr $b0 = $chunk[1]; 452*927933f5SAndreas Gohr $b1 = $chunk[2]; 453*927933f5SAndreas Gohr $b2 = $chunk[3]; 454*927933f5SAndreas Gohr $b3 = $chunk[4]; 455*927933f5SAndreas Gohr $b4 = $chunk[5]; 456*927933f5SAndreas Gohr $dest .= 457*927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 458*927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 459*927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 460*927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 461*927933f5SAndreas Gohr static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 462*927933f5SAndreas Gohr static::$method((($b3 >> 2) ) & 31) . 463*927933f5SAndreas Gohr static::$method((($b3 << 3) | ($b4 >> 5)) & 31) . 464*927933f5SAndreas Gohr static::$method( $b4 & 31); 465*927933f5SAndreas Gohr } 466*927933f5SAndreas Gohr // The last chunk, which may have padding: 467*927933f5SAndreas Gohr if ($i < $srcLen) { 468*927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 469*927933f5SAndreas Gohr $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i)); 470*927933f5SAndreas Gohr $b0 = $chunk[1]; 471*927933f5SAndreas Gohr if ($i + 3 < $srcLen) { 472*927933f5SAndreas Gohr $b1 = $chunk[2]; 473*927933f5SAndreas Gohr $b2 = $chunk[3]; 474*927933f5SAndreas Gohr $b3 = $chunk[4]; 475*927933f5SAndreas Gohr $dest .= 476*927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 477*927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 478*927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 479*927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 480*927933f5SAndreas Gohr static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 481*927933f5SAndreas Gohr static::$method((($b3 >> 2) ) & 31) . 482*927933f5SAndreas Gohr static::$method((($b3 << 3) ) & 31); 483*927933f5SAndreas Gohr if ($pad) { 484*927933f5SAndreas Gohr $dest .= '='; 485*927933f5SAndreas Gohr } 486*927933f5SAndreas Gohr } elseif ($i + 2 < $srcLen) { 487*927933f5SAndreas Gohr $b1 = $chunk[2]; 488*927933f5SAndreas Gohr $b2 = $chunk[3]; 489*927933f5SAndreas Gohr $dest .= 490*927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 491*927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 492*927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 493*927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 494*927933f5SAndreas Gohr static::$method((($b2 << 1) ) & 31); 495*927933f5SAndreas Gohr if ($pad) { 496*927933f5SAndreas Gohr $dest .= '==='; 497*927933f5SAndreas Gohr } 498*927933f5SAndreas Gohr } elseif ($i + 1 < $srcLen) { 499*927933f5SAndreas Gohr $b1 = $chunk[2]; 500*927933f5SAndreas Gohr $dest .= 501*927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 502*927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 503*927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 504*927933f5SAndreas Gohr static::$method((($b1 << 4) ) & 31); 505*927933f5SAndreas Gohr if ($pad) { 506*927933f5SAndreas Gohr $dest .= '===='; 507*927933f5SAndreas Gohr } 508*927933f5SAndreas Gohr } else { 509*927933f5SAndreas Gohr $dest .= 510*927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 511*927933f5SAndreas Gohr static::$method( ($b0 << 2) & 31); 512*927933f5SAndreas Gohr if ($pad) { 513*927933f5SAndreas Gohr $dest .= '======'; 514*927933f5SAndreas Gohr } 515*927933f5SAndreas Gohr } 516*927933f5SAndreas Gohr } 517*927933f5SAndreas Gohr return $dest; 518*927933f5SAndreas Gohr } 519*927933f5SAndreas Gohr} 520