1927933f5SAndreas Gohr<?php 2927933f5SAndreas Gohrdeclare(strict_types=1); 3927933f5SAndreas Gohrnamespace ParagonIE\ConstantTime; 4927933f5SAndreas Gohr 5927933f5SAndreas Gohruse InvalidArgumentException; 6*8e88a29bSAndreas Gohruse Override; 7927933f5SAndreas Gohruse RangeException; 8*8e88a29bSAndreas Gohruse SensitiveParameter; 9927933f5SAndreas Gohruse TypeError; 10*8e88a29bSAndreas Gohruse function pack; 11*8e88a29bSAndreas Gohruse function rtrim; 12*8e88a29bSAndreas Gohruse function strlen; 13*8e88a29bSAndreas Gohruse function substr; 14*8e88a29bSAndreas Gohruse function unpack; 15927933f5SAndreas Gohr 16927933f5SAndreas Gohr/** 17927933f5SAndreas Gohr * Copyright (c) 2016 - 2022 Paragon Initiative Enterprises. 18927933f5SAndreas Gohr * Copyright (c) 2014 Steve "Sc00bz" Thomas (steve at tobtu dot com) 19927933f5SAndreas Gohr * 20927933f5SAndreas Gohr * Permission is hereby granted, free of charge, to any person obtaining a copy 21927933f5SAndreas Gohr * of this software and associated documentation files (the "Software"), to deal 22927933f5SAndreas Gohr * in the Software without restriction, including without limitation the rights 23927933f5SAndreas Gohr * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 24927933f5SAndreas Gohr * copies of the Software, and to permit persons to whom the Software is 25927933f5SAndreas Gohr * furnished to do so, subject to the following conditions: 26927933f5SAndreas Gohr * 27927933f5SAndreas Gohr * The above copyright notice and this permission notice shall be included in all 28927933f5SAndreas Gohr * copies or substantial portions of the Software. 29927933f5SAndreas Gohr * 30927933f5SAndreas Gohr * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 31927933f5SAndreas Gohr * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 32927933f5SAndreas Gohr * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 33927933f5SAndreas Gohr * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34927933f5SAndreas Gohr * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 35927933f5SAndreas Gohr * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 36927933f5SAndreas Gohr * SOFTWARE. 37927933f5SAndreas Gohr */ 38927933f5SAndreas Gohr 39927933f5SAndreas Gohr/** 40927933f5SAndreas Gohr * Class Base32 41927933f5SAndreas Gohr * [A-Z][2-7] 42927933f5SAndreas Gohr * 43927933f5SAndreas Gohr * @package ParagonIE\ConstantTime 44927933f5SAndreas Gohr */ 45927933f5SAndreas Gohrabstract class Base32 implements EncoderInterface 46927933f5SAndreas Gohr{ 47927933f5SAndreas Gohr /** 48927933f5SAndreas Gohr * Decode a Base32-encoded string into raw binary 49927933f5SAndreas Gohr * 50927933f5SAndreas Gohr * @param string $encodedString 51927933f5SAndreas Gohr * @param bool $strictPadding 52927933f5SAndreas Gohr * @return string 53927933f5SAndreas Gohr */ 54*8e88a29bSAndreas Gohr #[Override] 55850e6620SAndreas Gohr public static function decode( 56*8e88a29bSAndreas Gohr #[SensitiveParameter] 57850e6620SAndreas Gohr string $encodedString, 58850e6620SAndreas Gohr bool $strictPadding = false 59850e6620SAndreas Gohr ): string { 60927933f5SAndreas Gohr return static::doDecode($encodedString, false, $strictPadding); 61927933f5SAndreas Gohr } 62927933f5SAndreas Gohr 63927933f5SAndreas Gohr /** 64927933f5SAndreas Gohr * Decode an uppercase Base32-encoded string into raw binary 65927933f5SAndreas Gohr * 66927933f5SAndreas Gohr * @param string $src 67927933f5SAndreas Gohr * @param bool $strictPadding 68927933f5SAndreas Gohr * @return string 69927933f5SAndreas Gohr */ 70850e6620SAndreas Gohr public static function decodeUpper( 71*8e88a29bSAndreas Gohr #[SensitiveParameter] 72850e6620SAndreas Gohr string $src, 73850e6620SAndreas Gohr bool $strictPadding = false 74850e6620SAndreas Gohr ): string { 75927933f5SAndreas Gohr return static::doDecode($src, true, $strictPadding); 76927933f5SAndreas Gohr } 77927933f5SAndreas Gohr 78927933f5SAndreas Gohr /** 79927933f5SAndreas Gohr * Encode into Base32 (RFC 4648) 80927933f5SAndreas Gohr * 81927933f5SAndreas Gohr * @param string $binString 82927933f5SAndreas Gohr * @return string 83927933f5SAndreas Gohr * @throws TypeError 84927933f5SAndreas Gohr */ 85*8e88a29bSAndreas Gohr #[Override] 86850e6620SAndreas Gohr public static function encode( 87*8e88a29bSAndreas Gohr #[SensitiveParameter] 88850e6620SAndreas Gohr string $binString 89850e6620SAndreas Gohr ): string { 90927933f5SAndreas Gohr return static::doEncode($binString, false, true); 91927933f5SAndreas Gohr } 92850e6620SAndreas Gohr 93927933f5SAndreas Gohr /** 94927933f5SAndreas Gohr * Encode into Base32 (RFC 4648) 95927933f5SAndreas Gohr * 96927933f5SAndreas Gohr * @param string $src 97927933f5SAndreas Gohr * @return string 98927933f5SAndreas Gohr * @throws TypeError 99*8e88a29bSAndreas Gohr * @api 100927933f5SAndreas Gohr */ 101850e6620SAndreas Gohr public static function encodeUnpadded( 102*8e88a29bSAndreas Gohr #[SensitiveParameter] 103850e6620SAndreas Gohr string $src 104850e6620SAndreas Gohr ): string { 105927933f5SAndreas Gohr return static::doEncode($src, false, false); 106927933f5SAndreas Gohr } 107927933f5SAndreas Gohr 108927933f5SAndreas Gohr /** 109927933f5SAndreas Gohr * Encode into uppercase Base32 (RFC 4648) 110927933f5SAndreas Gohr * 111927933f5SAndreas Gohr * @param string $src 112927933f5SAndreas Gohr * @return string 113927933f5SAndreas Gohr * @throws TypeError 114*8e88a29bSAndreas Gohr * @api 115927933f5SAndreas Gohr */ 116850e6620SAndreas Gohr public static function encodeUpper( 117*8e88a29bSAndreas Gohr #[SensitiveParameter] 118850e6620SAndreas Gohr string $src 119850e6620SAndreas Gohr ): string { 120927933f5SAndreas Gohr return static::doEncode($src, true, true); 121927933f5SAndreas Gohr } 122927933f5SAndreas Gohr 123927933f5SAndreas Gohr /** 124927933f5SAndreas Gohr * Encode into uppercase Base32 (RFC 4648) 125927933f5SAndreas Gohr * 126927933f5SAndreas Gohr * @param string $src 127927933f5SAndreas Gohr * @return string 128927933f5SAndreas Gohr * @throws TypeError 129*8e88a29bSAndreas Gohr * @api 130927933f5SAndreas Gohr */ 131850e6620SAndreas Gohr public static function encodeUpperUnpadded( 132*8e88a29bSAndreas Gohr #[SensitiveParameter] 133850e6620SAndreas Gohr string $src 134850e6620SAndreas Gohr ): string { 135927933f5SAndreas Gohr return static::doEncode($src, true, false); 136927933f5SAndreas Gohr } 137927933f5SAndreas Gohr 138927933f5SAndreas Gohr /** 139927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 5-bit integers 140927933f5SAndreas Gohr * into 8-bit integers. 141927933f5SAndreas Gohr * 142927933f5SAndreas Gohr * @param int $src 143927933f5SAndreas Gohr * @return int 144*8e88a29bSAndreas Gohr * @api 145927933f5SAndreas Gohr */ 146927933f5SAndreas Gohr protected static function decode5Bits(int $src): int 147927933f5SAndreas Gohr { 148927933f5SAndreas Gohr $ret = -1; 149927933f5SAndreas Gohr 150927933f5SAndreas Gohr // if ($src > 96 && $src < 123) $ret += $src - 97 + 1; // -64 151927933f5SAndreas Gohr $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 96); 152927933f5SAndreas Gohr 153927933f5SAndreas Gohr // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 154927933f5SAndreas Gohr $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 155927933f5SAndreas Gohr 156927933f5SAndreas Gohr return $ret; 157927933f5SAndreas Gohr } 158927933f5SAndreas Gohr 159927933f5SAndreas Gohr /** 160927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 5-bit integers 161927933f5SAndreas Gohr * into 8-bit integers. 162927933f5SAndreas Gohr * 163927933f5SAndreas Gohr * Uppercase variant. 164927933f5SAndreas Gohr * 165927933f5SAndreas Gohr * @param int $src 166927933f5SAndreas Gohr * @return int 167*8e88a29bSAndreas Gohr * @api 168927933f5SAndreas Gohr */ 169927933f5SAndreas Gohr protected static function decode5BitsUpper(int $src): int 170927933f5SAndreas Gohr { 171927933f5SAndreas Gohr $ret = -1; 172927933f5SAndreas Gohr 173927933f5SAndreas Gohr // if ($src > 64 && $src < 91) $ret += $src - 65 + 1; // -64 174927933f5SAndreas Gohr $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64); 175927933f5SAndreas Gohr 176927933f5SAndreas Gohr // if ($src > 0x31 && $src < 0x38) $ret += $src - 24 + 1; // -23 177927933f5SAndreas Gohr $ret += (((0x31 - $src) & ($src - 0x38)) >> 8) & ($src - 23); 178927933f5SAndreas Gohr 179927933f5SAndreas Gohr return $ret; 180927933f5SAndreas Gohr } 181927933f5SAndreas Gohr 182927933f5SAndreas Gohr /** 183927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 8-bit integers 184927933f5SAndreas Gohr * into 5-bit integers. 185927933f5SAndreas Gohr * 186927933f5SAndreas Gohr * @param int $src 187927933f5SAndreas Gohr * @return string 188*8e88a29bSAndreas Gohr * @api 189927933f5SAndreas Gohr */ 190927933f5SAndreas Gohr protected static function encode5Bits(int $src): string 191927933f5SAndreas Gohr { 192927933f5SAndreas Gohr $diff = 0x61; 193927933f5SAndreas Gohr 194927933f5SAndreas Gohr // if ($src > 25) $ret -= 72; 195927933f5SAndreas Gohr $diff -= ((25 - $src) >> 8) & 73; 196927933f5SAndreas Gohr 197*8e88a29bSAndreas Gohr return pack('C', $src + $diff); 198927933f5SAndreas Gohr } 199927933f5SAndreas Gohr 200927933f5SAndreas Gohr /** 201927933f5SAndreas Gohr * Uses bitwise operators instead of table-lookups to turn 8-bit integers 202927933f5SAndreas Gohr * into 5-bit integers. 203927933f5SAndreas Gohr * 204927933f5SAndreas Gohr * Uppercase variant. 205927933f5SAndreas Gohr * 206927933f5SAndreas Gohr * @param int $src 207927933f5SAndreas Gohr * @return string 208*8e88a29bSAndreas Gohr * @api 209927933f5SAndreas Gohr */ 210927933f5SAndreas Gohr protected static function encode5BitsUpper(int $src): string 211927933f5SAndreas Gohr { 212927933f5SAndreas Gohr $diff = 0x41; 213927933f5SAndreas Gohr 214927933f5SAndreas Gohr // if ($src > 25) $ret -= 40; 215927933f5SAndreas Gohr $diff -= ((25 - $src) >> 8) & 41; 216927933f5SAndreas Gohr 217*8e88a29bSAndreas Gohr return pack('C', $src + $diff); 218927933f5SAndreas Gohr } 219927933f5SAndreas Gohr 220927933f5SAndreas Gohr /** 221927933f5SAndreas Gohr * @param string $encodedString 222927933f5SAndreas Gohr * @param bool $upper 223927933f5SAndreas Gohr * @return string 224*8e88a29bSAndreas Gohr * @api 225927933f5SAndreas Gohr */ 226850e6620SAndreas Gohr public static function decodeNoPadding( 227*8e88a29bSAndreas Gohr #[SensitiveParameter] 228850e6620SAndreas Gohr string $encodedString, 229850e6620SAndreas Gohr bool $upper = false 230850e6620SAndreas Gohr ): string { 231*8e88a29bSAndreas Gohr $srcLen = strlen($encodedString); 232927933f5SAndreas Gohr if ($srcLen === 0) { 233927933f5SAndreas Gohr return ''; 234927933f5SAndreas Gohr } 235927933f5SAndreas Gohr if (($srcLen & 7) === 0) { 236927933f5SAndreas Gohr for ($j = 0; $j < 7 && $j < $srcLen; ++$j) { 237927933f5SAndreas Gohr if ($encodedString[$srcLen - $j - 1] === '=') { 238927933f5SAndreas Gohr throw new InvalidArgumentException( 239927933f5SAndreas Gohr "decodeNoPadding() doesn't tolerate padding" 240927933f5SAndreas Gohr ); 241927933f5SAndreas Gohr } 242927933f5SAndreas Gohr } 243927933f5SAndreas Gohr } 244927933f5SAndreas Gohr return static::doDecode( 245927933f5SAndreas Gohr $encodedString, 246927933f5SAndreas Gohr $upper, 247927933f5SAndreas Gohr true 248927933f5SAndreas Gohr ); 249927933f5SAndreas Gohr } 250927933f5SAndreas Gohr 251927933f5SAndreas Gohr /** 252927933f5SAndreas Gohr * Base32 decoding 253927933f5SAndreas Gohr * 254927933f5SAndreas Gohr * @param string $src 255927933f5SAndreas Gohr * @param bool $upper 256927933f5SAndreas Gohr * @param bool $strictPadding 257927933f5SAndreas Gohr * @return string 258927933f5SAndreas Gohr * 259927933f5SAndreas Gohr * @throws TypeError 260927933f5SAndreas Gohr */ 261927933f5SAndreas Gohr protected static function doDecode( 262*8e88a29bSAndreas Gohr #[SensitiveParameter] 263927933f5SAndreas Gohr string $src, 264927933f5SAndreas Gohr bool $upper = false, 265927933f5SAndreas Gohr bool $strictPadding = false 266927933f5SAndreas Gohr ): string { 267927933f5SAndreas Gohr // We do this to reduce code duplication: 268927933f5SAndreas Gohr $method = $upper 269927933f5SAndreas Gohr ? 'decode5BitsUpper' 270927933f5SAndreas Gohr : 'decode5Bits'; 271927933f5SAndreas Gohr 272927933f5SAndreas Gohr // Remove padding 273*8e88a29bSAndreas Gohr $srcLen = strlen($src); 274927933f5SAndreas Gohr if ($srcLen === 0) { 275927933f5SAndreas Gohr return ''; 276927933f5SAndreas Gohr } 277927933f5SAndreas Gohr if ($strictPadding) { 278927933f5SAndreas Gohr if (($srcLen & 7) === 0) { 279927933f5SAndreas Gohr for ($j = 0; $j < 7; ++$j) { 280927933f5SAndreas Gohr if ($src[$srcLen - 1] === '=') { 281927933f5SAndreas Gohr $srcLen--; 282927933f5SAndreas Gohr } else { 283927933f5SAndreas Gohr break; 284927933f5SAndreas Gohr } 285927933f5SAndreas Gohr } 286927933f5SAndreas Gohr } 287927933f5SAndreas Gohr if (($srcLen & 7) === 1) { 288927933f5SAndreas Gohr throw new RangeException( 289927933f5SAndreas Gohr 'Incorrect padding' 290927933f5SAndreas Gohr ); 291927933f5SAndreas Gohr } 292927933f5SAndreas Gohr } else { 293*8e88a29bSAndreas Gohr $src = rtrim($src, '='); 294*8e88a29bSAndreas Gohr $srcLen = strlen($src); 295927933f5SAndreas Gohr } 296927933f5SAndreas Gohr 297927933f5SAndreas Gohr $err = 0; 298927933f5SAndreas Gohr $dest = ''; 299927933f5SAndreas Gohr // Main loop (no padding): 300927933f5SAndreas Gohr for ($i = 0; $i + 8 <= $srcLen; $i += 8) { 301927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 302*8e88a29bSAndreas Gohr $chunk = unpack('C*', substr($src, $i, 8)); 303927933f5SAndreas Gohr /** @var int $c0 */ 304927933f5SAndreas Gohr $c0 = static::$method($chunk[1]); 305927933f5SAndreas Gohr /** @var int $c1 */ 306927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 307927933f5SAndreas Gohr /** @var int $c2 */ 308927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 309927933f5SAndreas Gohr /** @var int $c3 */ 310927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 311927933f5SAndreas Gohr /** @var int $c4 */ 312927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 313927933f5SAndreas Gohr /** @var int $c5 */ 314927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 315927933f5SAndreas Gohr /** @var int $c6 */ 316927933f5SAndreas Gohr $c6 = static::$method($chunk[7]); 317927933f5SAndreas Gohr /** @var int $c7 */ 318927933f5SAndreas Gohr $c7 = static::$method($chunk[8]); 319927933f5SAndreas Gohr 320*8e88a29bSAndreas Gohr $dest .= pack( 321927933f5SAndreas Gohr 'CCCCC', 322927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 323927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 324927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 325927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff, 326927933f5SAndreas Gohr (($c6 << 5) | ($c7 ) ) & 0xff 327927933f5SAndreas Gohr ); 328927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6 | $c7) >> 8; 329927933f5SAndreas Gohr } 330927933f5SAndreas Gohr // The last chunk, which may have padding: 331927933f5SAndreas Gohr if ($i < $srcLen) { 332927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 333*8e88a29bSAndreas Gohr $chunk = unpack('C*', substr($src, $i, $srcLen - $i)); 334927933f5SAndreas Gohr /** @var int $c0 */ 335927933f5SAndreas Gohr $c0 = static::$method($chunk[1]); 336927933f5SAndreas Gohr 337927933f5SAndreas Gohr if ($i + 6 < $srcLen) { 338927933f5SAndreas Gohr /** @var int $c1 */ 339927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 340927933f5SAndreas Gohr /** @var int $c2 */ 341927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 342927933f5SAndreas Gohr /** @var int $c3 */ 343927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 344927933f5SAndreas Gohr /** @var int $c4 */ 345927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 346927933f5SAndreas Gohr /** @var int $c5 */ 347927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 348927933f5SAndreas Gohr /** @var int $c6 */ 349927933f5SAndreas Gohr $c6 = static::$method($chunk[7]); 350927933f5SAndreas Gohr 351*8e88a29bSAndreas Gohr $dest .= pack( 352927933f5SAndreas Gohr 'CCCC', 353927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 354927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 355927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 356927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) | ($c6 >> 3)) & 0xff 357927933f5SAndreas Gohr ); 358927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5 | $c6) >> 8; 359927933f5SAndreas Gohr if ($strictPadding) { 360927933f5SAndreas Gohr $err |= ($c6 << 5) & 0xff; 361927933f5SAndreas Gohr } 362927933f5SAndreas Gohr } elseif ($i + 5 < $srcLen) { 363927933f5SAndreas Gohr /** @var int $c1 */ 364927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 365927933f5SAndreas Gohr /** @var int $c2 */ 366927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 367927933f5SAndreas Gohr /** @var int $c3 */ 368927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 369927933f5SAndreas Gohr /** @var int $c4 */ 370927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 371927933f5SAndreas Gohr /** @var int $c5 */ 372927933f5SAndreas Gohr $c5 = static::$method($chunk[6]); 373927933f5SAndreas Gohr 374*8e88a29bSAndreas Gohr $dest .= pack( 375927933f5SAndreas Gohr 'CCCC', 376927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 377927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 378927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff, 379927933f5SAndreas Gohr (($c4 << 7) | ($c5 << 2) ) & 0xff 380927933f5SAndreas Gohr ); 381927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4 | $c5) >> 8; 382927933f5SAndreas Gohr } elseif ($i + 4 < $srcLen) { 383927933f5SAndreas Gohr /** @var int $c1 */ 384927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 385927933f5SAndreas Gohr /** @var int $c2 */ 386927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 387927933f5SAndreas Gohr /** @var int $c3 */ 388927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 389927933f5SAndreas Gohr /** @var int $c4 */ 390927933f5SAndreas Gohr $c4 = static::$method($chunk[5]); 391927933f5SAndreas Gohr 392*8e88a29bSAndreas Gohr $dest .= pack( 393927933f5SAndreas Gohr 'CCC', 394927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 395927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff, 396927933f5SAndreas Gohr (($c3 << 4) | ($c4 >> 1) ) & 0xff 397927933f5SAndreas Gohr ); 398927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3 | $c4) >> 8; 399927933f5SAndreas Gohr if ($strictPadding) { 400927933f5SAndreas Gohr $err |= ($c4 << 7) & 0xff; 401927933f5SAndreas Gohr } 402927933f5SAndreas Gohr } elseif ($i + 3 < $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 /** @var int $c3 */ 408927933f5SAndreas Gohr $c3 = static::$method($chunk[4]); 409927933f5SAndreas Gohr 410*8e88a29bSAndreas Gohr $dest .= pack( 411927933f5SAndreas Gohr 'CC', 412927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 413927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) | ($c3 >> 4)) & 0xff 414927933f5SAndreas Gohr ); 415927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2 | $c3) >> 8; 416927933f5SAndreas Gohr if ($strictPadding) { 417927933f5SAndreas Gohr $err |= ($c3 << 4) & 0xff; 418927933f5SAndreas Gohr } 419927933f5SAndreas Gohr } elseif ($i + 2 < $srcLen) { 420927933f5SAndreas Gohr /** @var int $c1 */ 421927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 422927933f5SAndreas Gohr /** @var int $c2 */ 423927933f5SAndreas Gohr $c2 = static::$method($chunk[3]); 424927933f5SAndreas Gohr 425*8e88a29bSAndreas Gohr $dest .= pack( 426927933f5SAndreas Gohr 'CC', 427927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff, 428927933f5SAndreas Gohr (($c1 << 6) | ($c2 << 1) ) & 0xff 429927933f5SAndreas Gohr ); 430927933f5SAndreas Gohr $err |= ($c0 | $c1 | $c2) >> 8; 431927933f5SAndreas Gohr if ($strictPadding) { 432927933f5SAndreas Gohr $err |= ($c2 << 6) & 0xff; 433927933f5SAndreas Gohr } 434927933f5SAndreas Gohr } elseif ($i + 1 < $srcLen) { 435927933f5SAndreas Gohr /** @var int $c1 */ 436927933f5SAndreas Gohr $c1 = static::$method($chunk[2]); 437927933f5SAndreas Gohr 438*8e88a29bSAndreas Gohr $dest .= pack( 439927933f5SAndreas Gohr 'C', 440927933f5SAndreas Gohr (($c0 << 3) | ($c1 >> 2) ) & 0xff 441927933f5SAndreas Gohr ); 442927933f5SAndreas Gohr $err |= ($c0 | $c1) >> 8; 443927933f5SAndreas Gohr if ($strictPadding) { 444927933f5SAndreas Gohr $err |= ($c1 << 6) & 0xff; 445927933f5SAndreas Gohr } 446927933f5SAndreas Gohr } else { 447*8e88a29bSAndreas Gohr $dest .= pack( 448927933f5SAndreas Gohr 'C', 449927933f5SAndreas Gohr (($c0 << 3) ) & 0xff 450927933f5SAndreas Gohr ); 451927933f5SAndreas Gohr $err |= ($c0) >> 8; 452927933f5SAndreas Gohr } 453927933f5SAndreas Gohr } 454927933f5SAndreas Gohr $check = ($err === 0); 455927933f5SAndreas Gohr if (!$check) { 456927933f5SAndreas Gohr throw new RangeException( 457927933f5SAndreas Gohr 'Base32::doDecode() only expects characters in the correct base32 alphabet' 458927933f5SAndreas Gohr ); 459927933f5SAndreas Gohr } 460927933f5SAndreas Gohr return $dest; 461927933f5SAndreas Gohr } 462927933f5SAndreas Gohr 463927933f5SAndreas Gohr /** 464927933f5SAndreas Gohr * Base32 Encoding 465927933f5SAndreas Gohr * 466927933f5SAndreas Gohr * @param string $src 467927933f5SAndreas Gohr * @param bool $upper 468927933f5SAndreas Gohr * @param bool $pad 469927933f5SAndreas Gohr * @return string 470927933f5SAndreas Gohr * @throws TypeError 471927933f5SAndreas Gohr */ 472850e6620SAndreas Gohr protected static function doEncode( 473*8e88a29bSAndreas Gohr #[SensitiveParameter] 474850e6620SAndreas Gohr string $src, 475850e6620SAndreas Gohr bool $upper = false, 476*8e88a29bSAndreas Gohr bool $pad = true 477850e6620SAndreas Gohr ): string { 478927933f5SAndreas Gohr // We do this to reduce code duplication: 479927933f5SAndreas Gohr $method = $upper 480927933f5SAndreas Gohr ? 'encode5BitsUpper' 481927933f5SAndreas Gohr : 'encode5Bits'; 482927933f5SAndreas Gohr 483927933f5SAndreas Gohr $dest = ''; 484*8e88a29bSAndreas Gohr $srcLen = strlen($src); 485927933f5SAndreas Gohr 486927933f5SAndreas Gohr // Main loop (no padding): 487927933f5SAndreas Gohr for ($i = 0; $i + 5 <= $srcLen; $i += 5) { 488927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 489*8e88a29bSAndreas Gohr $chunk = unpack('C*', substr($src, $i, 5)); 490927933f5SAndreas Gohr $b0 = $chunk[1]; 491927933f5SAndreas Gohr $b1 = $chunk[2]; 492927933f5SAndreas Gohr $b2 = $chunk[3]; 493927933f5SAndreas Gohr $b3 = $chunk[4]; 494927933f5SAndreas Gohr $b4 = $chunk[5]; 495927933f5SAndreas Gohr $dest .= 496927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 497927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 498927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 499927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 500927933f5SAndreas Gohr static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 501927933f5SAndreas Gohr static::$method((($b3 >> 2) ) & 31) . 502927933f5SAndreas Gohr static::$method((($b3 << 3) | ($b4 >> 5)) & 31) . 503927933f5SAndreas Gohr static::$method( $b4 & 31); 504927933f5SAndreas Gohr } 505927933f5SAndreas Gohr // The last chunk, which may have padding: 506927933f5SAndreas Gohr if ($i < $srcLen) { 507927933f5SAndreas Gohr /** @var array<int, int> $chunk */ 508*8e88a29bSAndreas Gohr $chunk = unpack('C*', substr($src, $i, $srcLen - $i)); 509927933f5SAndreas Gohr $b0 = $chunk[1]; 510927933f5SAndreas Gohr if ($i + 3 < $srcLen) { 511927933f5SAndreas Gohr $b1 = $chunk[2]; 512927933f5SAndreas Gohr $b2 = $chunk[3]; 513927933f5SAndreas Gohr $b3 = $chunk[4]; 514927933f5SAndreas Gohr $dest .= 515927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 516927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 517927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 518927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 519927933f5SAndreas Gohr static::$method((($b2 << 1) | ($b3 >> 7)) & 31) . 520927933f5SAndreas Gohr static::$method((($b3 >> 2) ) & 31) . 521927933f5SAndreas Gohr static::$method((($b3 << 3) ) & 31); 522927933f5SAndreas Gohr if ($pad) { 523927933f5SAndreas Gohr $dest .= '='; 524927933f5SAndreas Gohr } 525927933f5SAndreas Gohr } elseif ($i + 2 < $srcLen) { 526927933f5SAndreas Gohr $b1 = $chunk[2]; 527927933f5SAndreas Gohr $b2 = $chunk[3]; 528927933f5SAndreas Gohr $dest .= 529927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 530927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 531927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 532927933f5SAndreas Gohr static::$method((($b1 << 4) | ($b2 >> 4)) & 31) . 533927933f5SAndreas Gohr static::$method((($b2 << 1) ) & 31); 534927933f5SAndreas Gohr if ($pad) { 535927933f5SAndreas Gohr $dest .= '==='; 536927933f5SAndreas Gohr } 537927933f5SAndreas Gohr } elseif ($i + 1 < $srcLen) { 538927933f5SAndreas Gohr $b1 = $chunk[2]; 539927933f5SAndreas Gohr $dest .= 540927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 541927933f5SAndreas Gohr static::$method((($b0 << 2) | ($b1 >> 6)) & 31) . 542927933f5SAndreas Gohr static::$method((($b1 >> 1) ) & 31) . 543927933f5SAndreas Gohr static::$method((($b1 << 4) ) & 31); 544927933f5SAndreas Gohr if ($pad) { 545927933f5SAndreas Gohr $dest .= '===='; 546927933f5SAndreas Gohr } 547927933f5SAndreas Gohr } else { 548927933f5SAndreas Gohr $dest .= 549927933f5SAndreas Gohr static::$method( ($b0 >> 3) & 31) . 550927933f5SAndreas Gohr static::$method( ($b0 << 2) & 31); 551927933f5SAndreas Gohr if ($pad) { 552927933f5SAndreas Gohr $dest .= '======'; 553927933f5SAndreas Gohr } 554927933f5SAndreas Gohr } 555927933f5SAndreas Gohr } 556927933f5SAndreas Gohr return $dest; 557927933f5SAndreas Gohr } 558927933f5SAndreas Gohr} 559