[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/defuse/php-encryption/src/ -> Encoding.php (source)

   1  <?php
   2  
   3  namespace Defuse\Crypto;
   4  
   5  use Defuse\Crypto\Exception as Ex;
   6  
   7  final class Encoding
   8  {
   9      const CHECKSUM_BYTE_SIZE     = 32;
  10      const CHECKSUM_HASH_ALGO     = 'sha256';
  11      const SERIALIZE_HEADER_BYTES = 4;
  12  
  13      /**
  14       * Converts a byte string to a hexadecimal string without leaking
  15       * information through side channels.
  16       *
  17       * @param string $byte_string
  18       *
  19       * @throws Ex\EnvironmentIsBrokenException
  20       *
  21       * @return string
  22       */
  23      public static function binToHex($byte_string)
  24      {
  25          $hex = '';
  26          $len = Core::ourStrlen($byte_string);
  27          for ($i = 0; $i < $len; ++$i) {
  28              $c = \ord($byte_string[$i]) & 0xf;
  29              $b = \ord($byte_string[$i]) >> 4;
  30              $hex .= \pack(
  31                  'CC',
  32                  87 + $b + ((($b - 10) >> 8) & ~38),
  33                  87 + $c + ((($c - 10) >> 8) & ~38)
  34              );
  35          }
  36          return $hex;
  37      }
  38  
  39      /**
  40       * Converts a hexadecimal string into a byte string without leaking
  41       * information through side channels.
  42       *
  43       * @param string $hex_string
  44       *
  45       * @throws Ex\BadFormatException
  46       * @throws Ex\EnvironmentIsBrokenException
  47       *
  48       * @return string
  49       * @psalm-suppress TypeDoesNotContainType
  50       */
  51      public static function hexToBin($hex_string)
  52      {
  53          $hex_pos = 0;
  54          $bin     = '';
  55          $hex_len = Core::ourStrlen($hex_string);
  56          $state   = 0;
  57          $c_acc   = 0;
  58  
  59          while ($hex_pos < $hex_len) {
  60              $c        = \ord($hex_string[$hex_pos]);
  61              $c_num    = $c ^ 48;
  62              $c_num0   = ($c_num - 10) >> 8;
  63              $c_alpha  = ($c & ~32) - 55;
  64              $c_alpha0 = (($c_alpha - 10) ^ ($c_alpha - 16)) >> 8;
  65              if (($c_num0 | $c_alpha0) === 0) {
  66                  throw new Ex\BadFormatException(
  67                      'Encoding::hexToBin() input is not a hex string.'
  68                  );
  69              }
  70              $c_val = ($c_num0 & $c_num) | ($c_alpha & $c_alpha0);
  71              if ($state === 0) {
  72                  $c_acc = $c_val * 16;
  73              } else {
  74                  $bin .= \pack('C', $c_acc | $c_val);
  75              }
  76              $state ^= 1;
  77              ++$hex_pos;
  78          }
  79          return $bin;
  80      }
  81      
  82      /**
  83       * Remove trialing whitespace without table look-ups or branches.
  84       *
  85       * Calling this function may leak the length of the string as well as the
  86       * number of trailing whitespace characters through side-channels.
  87       *
  88       * @param string $string
  89       * @return string
  90       */
  91      public static function trimTrailingWhitespace($string = '')
  92      {
  93          $length = Core::ourStrlen($string);
  94          if ($length < 1) {
  95              return '';
  96          }
  97          do {
  98              $prevLength = $length;
  99              $last = $length - 1;
 100              $chr = \ord($string[$last]);
 101  
 102              /* Null Byte (0x00), a.k.a. \0 */
 103              // if ($chr === 0x00) $length -= 1;
 104              $sub = (($chr - 1) >> 8 ) & 1;
 105              $length -= $sub;
 106              $last -= $sub;
 107  
 108              /* Horizontal Tab (0x09) a.k.a. \t */
 109              $chr = \ord($string[$last]);
 110              // if ($chr === 0x09) $length -= 1;
 111              $sub = (((0x08 - $chr) & ($chr - 0x0a)) >> 8) & 1;
 112              $length -= $sub;
 113              $last -= $sub;
 114  
 115              /* New Line (0x0a), a.k.a. \n */
 116              $chr = \ord($string[$last]);
 117              // if ($chr === 0x0a) $length -= 1;
 118              $sub = (((0x09 - $chr) & ($chr - 0x0b)) >> 8) & 1;
 119              $length -= $sub;
 120              $last -= $sub;
 121  
 122              /* Carriage Return (0x0D), a.k.a. \r */
 123              $chr = \ord($string[$last]);
 124              // if ($chr === 0x0d) $length -= 1;
 125              $sub = (((0x0c - $chr) & ($chr - 0x0e)) >> 8) & 1;
 126              $length -= $sub;
 127              $last -= $sub;
 128  
 129              /* Space */
 130              $chr = \ord($string[$last]);
 131              // if ($chr === 0x20) $length -= 1;
 132              $sub = (((0x1f - $chr) & ($chr - 0x21)) >> 8) & 1;
 133              $length -= $sub;
 134          } while ($prevLength !== $length && $length > 0);
 135          return (string) Core::ourSubstr($string, 0, $length);
 136      }
 137  
 138      /*
 139       * SECURITY NOTE ON APPLYING CHECKSUMS TO SECRETS:
 140       *
 141       *      The checksum introduces a potential security weakness. For example,
 142       *      suppose we apply a checksum to a key, and that an adversary has an
 143       *      exploit against the process containing the key, such that they can
 144       *      overwrite an arbitrary byte of memory and then cause the checksum to
 145       *      be verified and learn the result.
 146       *
 147       *      In this scenario, the adversary can extract the key one byte at
 148       *      a time by overwriting it with their guess of its value and then
 149       *      asking if the checksum matches. If it does, their guess was right.
 150       *      This kind of attack may be more easy to implement and more reliable
 151       *      than a remote code execution attack.
 152       *
 153       *      This attack also applies to authenticated encryption as a whole, in
 154       *      the situation where the adversary can overwrite a byte of the key
 155       *      and then cause a valid ciphertext to be decrypted, and then
 156       *      determine whether the MAC check passed or failed.
 157       *
 158       *      By using the full SHA256 hash instead of truncating it, I'm ensuring
 159       *      that both ways of going about the attack are equivalently difficult.
 160       *      A shorter checksum of say 32 bits might be more useful to the
 161       *      adversary as an oracle in case their writes are coarser grained.
 162       *
 163       *      Because the scenario assumes a serious vulnerability, we don't try
 164       *      to prevent attacks of this style.
 165       */
 166  
 167      /**
 168       * INTERNAL USE ONLY: Applies a version header, applies a checksum, and
 169       * then encodes a byte string into a range of printable ASCII characters.
 170       *
 171       * @param string $header
 172       * @param string $bytes
 173       *
 174       * @throws Ex\EnvironmentIsBrokenException
 175       *
 176       * @return string
 177       */
 178      public static function saveBytesToChecksummedAsciiSafeString($header, $bytes)
 179      {
 180          // Headers must be a constant length to prevent one type's header from
 181          // being a prefix of another type's header, leading to ambiguity.
 182          Core::ensureTrue(
 183              Core::ourStrlen($header) === self::SERIALIZE_HEADER_BYTES,
 184              'Header must be ' . self::SERIALIZE_HEADER_BYTES . ' bytes.'
 185          );
 186  
 187          return Encoding::binToHex(
 188              $header .
 189              $bytes .
 190              \hash(
 191                  self::CHECKSUM_HASH_ALGO,
 192                  $header . $bytes,
 193                  true
 194              )
 195          );
 196      }
 197  
 198      /**
 199       * INTERNAL USE ONLY: Decodes, verifies the header and checksum, and returns
 200       * the encoded byte string.
 201       *
 202       * @param string $expected_header
 203       * @param string $string
 204       *
 205       * @throws Ex\EnvironmentIsBrokenException
 206       * @throws Ex\BadFormatException
 207       *
 208       * @return string
 209       */
 210      public static function loadBytesFromChecksummedAsciiSafeString($expected_header, $string)
 211      {
 212          // Headers must be a constant length to prevent one type's header from
 213          // being a prefix of another type's header, leading to ambiguity.
 214          Core::ensureTrue(
 215              Core::ourStrlen($expected_header) === self::SERIALIZE_HEADER_BYTES,
 216              'Header must be 4 bytes.'
 217          );
 218  
 219          /* If you get an exception here when attempting to load from a file, first pass your
 220             key to Encoding::trimTrailingWhitespace() to remove newline characters, etc.      */
 221          $bytes = Encoding::hexToBin($string);
 222  
 223          /* Make sure we have enough bytes to get the version header and checksum. */
 224          if (Core::ourStrlen($bytes) < self::SERIALIZE_HEADER_BYTES + self::CHECKSUM_BYTE_SIZE) {
 225              throw new Ex\BadFormatException(
 226                  'Encoded data is shorter than expected.'
 227              );
 228          }
 229  
 230          /* Grab the version header. */
 231          $actual_header = (string) Core::ourSubstr($bytes, 0, self::SERIALIZE_HEADER_BYTES);
 232  
 233          if ($actual_header !== $expected_header) {
 234              throw new Ex\BadFormatException(
 235                  'Invalid header.'
 236              );
 237          }
 238  
 239          /* Grab the bytes that are part of the checksum. */
 240          $checked_bytes = (string) Core::ourSubstr(
 241              $bytes,
 242              0,
 243              Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE
 244          );
 245  
 246          /* Grab the included checksum. */
 247          $checksum_a = (string) Core::ourSubstr(
 248              $bytes,
 249              Core::ourStrlen($bytes) - self::CHECKSUM_BYTE_SIZE,
 250              self::CHECKSUM_BYTE_SIZE
 251          );
 252  
 253          /* Re-compute the checksum. */
 254          $checksum_b = \hash(self::CHECKSUM_HASH_ALGO, $checked_bytes, true);
 255  
 256          /* Check if the checksum matches. */
 257          if (! Core::hashEquals($checksum_a, $checksum_b)) {
 258              throw new Ex\BadFormatException(
 259                  "Data is corrupted, the checksum doesn't match"
 260              );
 261          }
 262  
 263          return (string) Core::ourSubstr(
 264              $bytes,
 265              self::SERIALIZE_HEADER_BYTES,
 266              Core::ourStrlen($bytes) - self::SERIALIZE_HEADER_BYTES - self::CHECKSUM_BYTE_SIZE
 267          );
 268      }
 269  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer