[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/vendor/paragonie/constant_time_encoding/src/ -> Base64.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 Base64
  34   * [A-Z][a-z][0-9]+/
  35   *
  36   * @package ParagonIE\ConstantTime
  37   */
  38  abstract class Base64 implements EncoderInterface
  39  {
  40      /**
  41       * Encode into Base64
  42       *
  43       * Base64 character set "[A-Z][a-z][0-9]+/"
  44       *
  45       * @param string $binString
  46       * @return string
  47       *
  48       * @throws TypeError
  49       */
  50      public static function encode(string $binString): string
  51      {
  52          return static::doEncode($binString, true);
  53      }
  54  
  55      /**
  56       * Encode into Base64, no = padding
  57       *
  58       * Base64 character set "[A-Z][a-z][0-9]+/"
  59       *
  60       * @param string $src
  61       * @return string
  62       *
  63       * @throws TypeError
  64       */
  65      public static function encodeUnpadded(string $src): string
  66      {
  67          return static::doEncode($src, false);
  68      }
  69  
  70      /**
  71       * @param string $src
  72       * @param bool $pad   Include = padding?
  73       * @return string
  74       *
  75       * @throws TypeError
  76       */
  77      protected static function doEncode(string $src, bool $pad = true): string
  78      {
  79          $dest = '';
  80          $srcLen = Binary::safeStrlen($src);
  81          // Main loop (no padding):
  82          for ($i = 0; $i + 3 <= $srcLen; $i += 3) {
  83              /** @var array<int, int> $chunk */
  84              $chunk = \unpack('C*', Binary::safeSubstr($src, $i, 3));
  85              $b0 = $chunk[1];
  86              $b1 = $chunk[2];
  87              $b2 = $chunk[3];
  88  
  89              $dest .=
  90                  static::encode6Bits(               $b0 >> 2       ) .
  91                  static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
  92                  static::encode6Bits((($b1 << 2) | ($b2 >> 6)) & 63) .
  93                  static::encode6Bits(  $b2                     & 63);
  94          }
  95          // The last chunk, which may have padding:
  96          if ($i < $srcLen) {
  97              /** @var array<int, int> $chunk */
  98              $chunk = \unpack('C*', Binary::safeSubstr($src, $i, $srcLen - $i));
  99              $b0 = $chunk[1];
 100              if ($i + 1 < $srcLen) {
 101                  $b1 = $chunk[2];
 102                  $dest .=
 103                      static::encode6Bits($b0 >> 2) .
 104                      static::encode6Bits((($b0 << 4) | ($b1 >> 4)) & 63) .
 105                      static::encode6Bits(($b1 << 2) & 63);
 106                  if ($pad) {
 107                      $dest .= '=';
 108                  }
 109              } else {
 110                  $dest .=
 111                      static::encode6Bits( $b0 >> 2) .
 112                      static::encode6Bits(($b0 << 4) & 63);
 113                  if ($pad) {
 114                      $dest .= '==';
 115                  }
 116              }
 117          }
 118          return $dest;
 119      }
 120  
 121      /**
 122       * decode from base64 into binary
 123       *
 124       * Base64 character set "./[A-Z][a-z][0-9]"
 125       *
 126       * @param string $encodedString
 127       * @param bool $strictPadding
 128       * @return string
 129       *
 130       * @throws RangeException
 131       * @throws TypeError
 132       * @psalm-suppress RedundantCondition
 133       */
 134      public static function decode(string $encodedString, bool $strictPadding = false): string
 135      {
 136          // Remove padding
 137          $srcLen = Binary::safeStrlen($encodedString);
 138          if ($srcLen === 0) {
 139              return '';
 140          }
 141  
 142          if ($strictPadding) {
 143              if (($srcLen & 3) === 0) {
 144                  if ($encodedString[$srcLen - 1] === '=') {
 145                      $srcLen--;
 146                      if ($encodedString[$srcLen - 1] === '=') {
 147                          $srcLen--;
 148                      }
 149                  }
 150              }
 151              if (($srcLen & 3) === 1) {
 152                  throw new RangeException(
 153                      'Incorrect padding'
 154                  );
 155              }
 156              if ($encodedString[$srcLen - 1] === '=') {
 157                  throw new RangeException(
 158                      'Incorrect padding'
 159                  );
 160              }
 161          } else {
 162              $encodedString = \rtrim($encodedString, '=');
 163              $srcLen = Binary::safeStrlen($encodedString);
 164          }
 165  
 166          $err = 0;
 167          $dest = '';
 168          // Main loop (no padding):
 169          for ($i = 0; $i + 4 <= $srcLen; $i += 4) {
 170              /** @var array<int, int> $chunk */
 171              $chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, 4));
 172              $c0 = static::decode6Bits($chunk[1]);
 173              $c1 = static::decode6Bits($chunk[2]);
 174              $c2 = static::decode6Bits($chunk[3]);
 175              $c3 = static::decode6Bits($chunk[4]);
 176  
 177              $dest .= \pack(
 178                  'CCC',
 179                  ((($c0 << 2) | ($c1 >> 4)) & 0xff),
 180                  ((($c1 << 4) | ($c2 >> 2)) & 0xff),
 181                  ((($c2 << 6) |  $c3      ) & 0xff)
 182              );
 183              $err |= ($c0 | $c1 | $c2 | $c3) >> 8;
 184          }
 185          // The last chunk, which may have padding:
 186          if ($i < $srcLen) {
 187              /** @var array<int, int> $chunk */
 188              $chunk = \unpack('C*', Binary::safeSubstr($encodedString, $i, $srcLen - $i));
 189              $c0 = static::decode6Bits($chunk[1]);
 190  
 191              if ($i + 2 < $srcLen) {
 192                  $c1 = static::decode6Bits($chunk[2]);
 193                  $c2 = static::decode6Bits($chunk[3]);
 194                  $dest .= \pack(
 195                      'CC',
 196                      ((($c0 << 2) | ($c1 >> 4)) & 0xff),
 197                      ((($c1 << 4) | ($c2 >> 2)) & 0xff)
 198                  );
 199                  $err |= ($c0 | $c1 | $c2) >> 8;
 200                  if ($strictPadding) {
 201                      $err |= ($c2 << 6) & 0xff;
 202                  }
 203              } elseif ($i + 1 < $srcLen) {
 204                  $c1 = static::decode6Bits($chunk[2]);
 205                  $dest .= \pack(
 206                      'C',
 207                      ((($c0 << 2) | ($c1 >> 4)) & 0xff)
 208                  );
 209                  $err |= ($c0 | $c1) >> 8;
 210                  if ($strictPadding) {
 211                      $err |= ($c1 << 4) & 0xff;
 212                  }
 213              } elseif ($strictPadding) {
 214                  $err |= 1;
 215              }
 216          }
 217          $check = ($err === 0);
 218          if (!$check) {
 219              throw new RangeException(
 220                  'Base64::decode() only expects characters in the correct base64 alphabet'
 221              );
 222          }
 223          return $dest;
 224      }
 225  
 226      /**
 227       * @param string $encodedString
 228       * @return string
 229       */
 230      public static function decodeNoPadding(string $encodedString): string
 231      {
 232          $srcLen = Binary::safeStrlen($encodedString);
 233          if ($srcLen === 0) {
 234              return '';
 235          }
 236          if (($srcLen & 3) === 0) {
 237              if ($encodedString[$srcLen - 1] === '=') {
 238                  throw new InvalidArgumentException(
 239                      "decodeNoPadding() doesn't tolerate padding"
 240                  );
 241              }
 242              if (($srcLen & 3) > 1) {
 243                  if ($encodedString[$srcLen - 2] === '=') {
 244                      throw new InvalidArgumentException(
 245                          "decodeNoPadding() doesn't tolerate padding"
 246                      );
 247                  }
 248              }
 249          }
 250          return static::decode(
 251              $encodedString,
 252              true
 253          );
 254      }
 255  
 256      /**
 257       * Uses bitwise operators instead of table-lookups to turn 6-bit integers
 258       * into 8-bit integers.
 259       *
 260       * Base64 character set:
 261       * [A-Z]      [a-z]      [0-9]      +     /
 262       * 0x41-0x5a, 0x61-0x7a, 0x30-0x39, 0x2b, 0x2f
 263       *
 264       * @param int $src
 265       * @return int
 266       */
 267      protected static function decode6Bits(int $src): int
 268      {
 269          $ret = -1;
 270  
 271          // if ($src > 0x40 && $src < 0x5b) $ret += $src - 0x41 + 1; // -64
 272          $ret += (((0x40 - $src) & ($src - 0x5b)) >> 8) & ($src - 64);
 273  
 274          // if ($src > 0x60 && $src < 0x7b) $ret += $src - 0x61 + 26 + 1; // -70
 275          $ret += (((0x60 - $src) & ($src - 0x7b)) >> 8) & ($src - 70);
 276  
 277          // if ($src > 0x2f && $src < 0x3a) $ret += $src - 0x30 + 52 + 1; // 5
 278          $ret += (((0x2f - $src) & ($src - 0x3a)) >> 8) & ($src + 5);
 279  
 280          // if ($src == 0x2b) $ret += 62 + 1;
 281          $ret += (((0x2a - $src) & ($src - 0x2c)) >> 8) & 63;
 282  
 283          // if ($src == 0x2f) ret += 63 + 1;
 284          $ret += (((0x2e - $src) & ($src - 0x30)) >> 8) & 64;
 285  
 286          return $ret;
 287      }
 288  
 289      /**
 290       * Uses bitwise operators instead of table-lookups to turn 8-bit integers
 291       * into 6-bit integers.
 292       *
 293       * @param int $src
 294       * @return string
 295       */
 296      protected static function encode6Bits(int $src): string
 297      {
 298          $diff = 0x41;
 299  
 300          // if ($src > 25) $diff += 0x61 - 0x41 - 26; // 6
 301          $diff += ((25 - $src) >> 8) & 6;
 302  
 303          // if ($src > 51) $diff += 0x30 - 0x61 - 26; // -75
 304          $diff -= ((51 - $src) >> 8) & 75;
 305  
 306          // if ($src > 61) $diff += 0x2b - 0x30 - 10; // -15
 307          $diff -= ((61 - $src) >> 8) & 15;
 308  
 309          // if ($src > 62) $diff += 0x2f - 0x2b - 1; // 3
 310          $diff += ((62 - $src) >> 8) & 3;
 311  
 312          return \pack('C', $src + $diff);
 313      }
 314  }