[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/paragonie/constant_time_encoding/src/ -> Base32.php (source)

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