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