[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/phpseclib/phpseclib/phpseclib/Crypt/ -> Salsa20.php (source)

   1  <?php
   2  
   3  /**
   4   * Pure-PHP implementation of Salsa20.
   5   *
   6   * PHP version 5
   7   *
   8   * @category  Crypt
   9   * @package   Salsa20
  10   * @author    Jim Wigginton <[email protected]>
  11   * @copyright 2019 Jim Wigginton
  12   * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
  13   * @link      http://phpseclib.sourceforge.net
  14   */
  15  
  16  namespace phpseclib3\Crypt;
  17  
  18  use phpseclib3\Common\Functions\Strings;
  19  use phpseclib3\Crypt\Common\StreamCipher;
  20  use phpseclib3\Exception\BadDecryptionException;
  21  use phpseclib3\Exception\InsufficientSetupException;
  22  
  23  /**
  24   * Pure-PHP implementation of Salsa20.
  25   *
  26   * @package Salsa20
  27   * @author  Jim Wigginton <[email protected]>
  28   * @access  public
  29   */
  30  class Salsa20 extends StreamCipher
  31  {
  32      /**
  33       * Part 1 of the state
  34       *
  35       * @var string|false
  36       */
  37      protected $p1 = false;
  38  
  39      /**
  40       * Part 2 of the state
  41       *
  42       * @var string|false
  43       */
  44      protected $p2 = false;
  45  
  46      /**
  47       * Key Length (in bytes)
  48       *
  49       * @var int
  50       */
  51      protected $key_length = 32; // = 256 bits
  52  
  53      /**
  54       * @access private
  55       * @see \phpseclib3\Crypt\Salsa20::crypt()
  56       */
  57      const ENCRYPT = 0;
  58  
  59      /**
  60       * @access private
  61       * @see \phpseclib3\Crypt\Salsa20::crypt()
  62       */
  63      const DECRYPT = 1;
  64  
  65      /**
  66       * Encryption buffer for continuous mode
  67       *
  68       * @var array
  69       */
  70      protected $enbuffer;
  71  
  72      /**
  73       * Decryption buffer for continuous mode
  74       *
  75       * @var array
  76       */
  77      protected $debuffer;
  78  
  79      /**
  80       * Counter
  81       *
  82       * @var int
  83       */
  84      protected $counter = 0;
  85  
  86      /**
  87       * Using Generated Poly1305 Key
  88       *
  89       * @var boolean
  90       */
  91      protected $usingGeneratedPoly1305Key = false;
  92  
  93      /**
  94       * Salsa20 uses a nonce
  95       *
  96       * @return bool
  97       */
  98      public function usesNonce()
  99      {
 100          return true;
 101      }
 102  
 103      /**
 104       * Sets the key.
 105       *
 106       * @param string $key
 107       * @throws \LengthException if the key length isn't supported
 108       */
 109      public function setKey($key)
 110      {
 111          switch (strlen($key)) {
 112              case 16:
 113              case 32:
 114                  break;
 115              default:
 116                  throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes 16 or 32 are supported');
 117          }
 118  
 119          parent::setKey($key);
 120      }
 121  
 122      /**
 123       * Sets the nonce.
 124       *
 125       * @param string $nonce
 126       */
 127      public function setNonce($nonce)
 128      {
 129          if (strlen($nonce) != 8) {
 130              throw new \LengthException('Nonce of size ' . strlen($key) . ' not supported by this algorithm. Only an 64-bit nonce is supported');
 131          }
 132  
 133          $this->nonce = $nonce;
 134          $this->changed = true;
 135          $this->setEngine();
 136      }
 137  
 138      /**
 139       * Sets the counter.
 140       *
 141       * @param int $counter
 142       */
 143      public function setCounter($counter)
 144      {
 145          $this->counter = $counter;
 146          $this->setEngine();
 147      }
 148  
 149      /**
 150       * Creates a Poly1305 key using the method discussed in RFC8439
 151       *
 152       * See https://tools.ietf.org/html/rfc8439#section-2.6.1
 153       */
 154      protected function createPoly1305Key()
 155      {
 156          if ($this->nonce === false) {
 157              throw new InsufficientSetupException('No nonce has been defined');
 158          }
 159  
 160          if ($this->key === false) {
 161              throw new InsufficientSetupException('No key has been defined');
 162          }
 163  
 164          $c = clone $this;
 165          $c->setCounter(0);
 166          $c->usePoly1305 = false;
 167          $block = $c->encrypt(str_repeat("\0", 256));
 168          $this->setPoly1305Key(substr($block, 0, 32));
 169  
 170          if ($this->counter == 0) {
 171              $this->counter++;
 172          }
 173      }
 174  
 175      /**
 176       * Setup the self::ENGINE_INTERNAL $engine
 177       *
 178       * (re)init, if necessary, the internal cipher $engine
 179       *
 180       * _setup() will be called each time if $changed === true
 181       * typically this happens when using one or more of following public methods:
 182       *
 183       * - setKey()
 184       *
 185       * - setNonce()
 186       *
 187       * - First run of encrypt() / decrypt() with no init-settings
 188       *
 189       * @see self::setKey()
 190       * @see self::setNonce()
 191       * @see self::disableContinuousBuffer()
 192       */
 193      protected function setup()
 194      {
 195          if (!$this->changed) {
 196              return;
 197          }
 198  
 199          $this->enbuffer = $this->debuffer = ['ciphertext' => '', 'counter' => $this->counter];
 200  
 201          $this->changed = $this->nonIVChanged = false;
 202  
 203          if ($this->nonce === false) {
 204              throw new InsufficientSetupException('No nonce has been defined');
 205          }
 206  
 207          if ($this->key === false) {
 208              throw new InsufficientSetupException('No key has been defined');
 209          }
 210  
 211          if ($this->usePoly1305 && !isset($this->poly1305Key)) {
 212              $this->usingGeneratedPoly1305Key = true;
 213              $this->createPoly1305Key();
 214          }
 215  
 216          $key = $this->key;
 217          if (strlen($key) == 16) {
 218              $constant = 'expand 16-byte k';
 219              $key .= $key;
 220          } else {
 221              $constant = 'expand 32-byte k';
 222          }
 223  
 224          $this->p1 = substr($constant, 0, 4) .
 225                      substr($key, 0, 16) .
 226                      substr($constant, 4, 4) .
 227                      $this->nonce .
 228                      "\0\0\0\0";
 229          $this->p2 = substr($constant, 8, 4) .
 230                      substr($key, 16, 16) .
 231                      substr($constant, 12, 4);
 232      }
 233  
 234      /**
 235       * Setup the key (expansion)
 236       */
 237      protected function setupKey()
 238      {
 239          // Salsa20 does not utilize this method
 240      }
 241  
 242      /**
 243       * Encrypts a message.
 244       *
 245       * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
 246       * @see self::crypt()
 247       * @param string $plaintext
 248       * @return string $ciphertext
 249       */
 250      public function encrypt($plaintext)
 251      {
 252          $ciphertext = $this->crypt($plaintext, self::ENCRYPT);
 253          if (isset($this->poly1305Key)) {
 254              $this->newtag = $this->poly1305($ciphertext);
 255          }
 256          return $ciphertext;
 257      }
 258  
 259      /**
 260       * Decrypts a message.
 261       *
 262       * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
 263       * At least if the continuous buffer is disabled.
 264       *
 265       * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
 266       * @see self::crypt()
 267       * @param string $ciphertext
 268       * @return string $plaintext
 269       */
 270      public function decrypt($ciphertext)
 271      {
 272          if (isset($this->poly1305Key)) {
 273              if ($this->oldtag === false) {
 274                  throw new InsufficientSetupException('Authentication Tag has not been set');
 275              }
 276              $newtag = $this->poly1305($ciphertext);
 277              if ($this->oldtag != substr($newtag, 0, strlen($this->oldtag))) {
 278                  $this->oldtag = false;
 279                  throw new BadDecryptionException('Derived authentication tag and supplied authentication tag do not match');
 280              }
 281              $this->oldtag = false;
 282          }
 283  
 284          return $this->crypt($ciphertext, self::DECRYPT);
 285      }
 286  
 287      /**
 288       * Encrypts a block
 289       *
 290       * @param string $in
 291       */
 292      protected function encryptBlock($in)
 293      {
 294          // Salsa20 does not utilize this method
 295      }
 296  
 297      /**
 298       * Decrypts a block
 299       *
 300       * @param string $in
 301       */
 302      protected function decryptBlock($in)
 303      {
 304          // Salsa20 does not utilize this method
 305      }
 306  
 307      /**
 308       * Encrypts or decrypts a message.
 309       *
 310       * @see self::encrypt()
 311       * @see self::decrypt()
 312       * @param string $text
 313       * @param int $mode
 314       * @return string $text
 315       */
 316      private function crypt($text, $mode)
 317      {
 318          $this->setup();
 319          if (!$this->continuousBuffer) {
 320              if ($this->engine == self::ENGINE_OPENSSL) {
 321                  $iv = pack('V', $this->counter) . $this->p2;
 322                  return openssl_encrypt(
 323                      $text,
 324                      $this->cipher_name_openssl,
 325                      $this->key,
 326                      OPENSSL_RAW_DATA,
 327                      $iv
 328                  );
 329              }
 330              $i = $this->counter;
 331              $blocks = str_split($text, 64);
 332              foreach ($blocks as &$block) {
 333                  $block ^= static::salsa20($this->p1 . pack('V', $i++) . $this->p2);
 334              }
 335  
 336              return implode('', $blocks);
 337          }
 338  
 339          if ($mode == self::ENCRYPT) {
 340              $buffer = &$this->enbuffer;
 341          } else {
 342              $buffer = &$this->debuffer;
 343          }
 344          if (!strlen($buffer['ciphertext'])) {
 345              $ciphertext = '';
 346          } else {
 347              $ciphertext = $text ^ Strings::shift($buffer['ciphertext'], strlen($text));
 348              $text = substr($text, strlen($ciphertext));
 349              if (!strlen($text)) {
 350                  return $ciphertext;
 351              }
 352          }
 353  
 354          $overflow = strlen($text) % 64; // & 0x3F
 355          if ($overflow) {
 356              $text2 = Strings::pop($text, $overflow);
 357              if ($this->engine == self::ENGINE_OPENSSL) {
 358                  $iv = pack('V', $buffer['counter']) . $this->p2;
 359                  // at this point $text should be a multiple of 64
 360                  $buffer['counter'] += (strlen($text) >> 6) + 1; // ie. divide by 64
 361                  $encrypted = openssl_encrypt(
 362                      $text . str_repeat("\0", 64),
 363                      $this->cipher_name_openssl,
 364                      $this->key,
 365                      OPENSSL_RAW_DATA,
 366                      $iv
 367                  );
 368                  $temp = Strings::pop($encrypted, 64);
 369              } else {
 370                  $blocks = str_split($text, 64);
 371                  if (strlen($text)) {
 372                      foreach ($blocks as &$block) {
 373                          $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
 374                      }
 375                  }
 376                  $encrypted = implode('', $blocks);
 377                  $temp = static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
 378              }
 379              $ciphertext .= $encrypted . ($text2 ^ $temp);
 380              $buffer['ciphertext'] = substr($temp, $overflow);
 381          } elseif (!strlen($buffer['ciphertext'])) {
 382              if ($this->engine == self::ENGINE_OPENSSL) {
 383                  $iv = pack('V', $buffer['counter']) . $this->p2;
 384                  $buffer['counter'] += (strlen($text) >> 6);
 385                  $ciphertext .= openssl_encrypt(
 386                      $text,
 387                      $this->cipher_name_openssl,
 388                      $this->key,
 389                      OPENSSL_RAW_DATA,
 390                      $iv
 391                  );
 392              } else {
 393                  $blocks = str_split($text, 64);
 394                  foreach ($blocks as &$block) {
 395                      $block ^= static::salsa20($this->p1 . pack('V', $buffer['counter']++) . $this->p2);
 396                  }
 397                  $ciphertext .= implode('', $blocks);
 398              }
 399          }
 400  
 401          return $ciphertext;
 402      }
 403  
 404      /**
 405       * Left Rotate
 406       *
 407       * @param int $x
 408       * @param int $n
 409       * @return int
 410       */
 411      protected static function leftRotate($x, $n)
 412      {
 413          $r1 = $x << $n;
 414          if (PHP_INT_SIZE == 8) {
 415              $r1 &= 0xFFFFFFFF;
 416              $r2 = ($x & 0xFFFFFFFF) >> (32 - $n);
 417          } else {
 418              $r2 = $x >> (32 - $n);
 419              $r2 &= (1 << $n) - 1;
 420          }
 421          return $r1 | $r2;
 422      }
 423  
 424      /**
 425       * The quarterround function
 426       *
 427       * @param int $a
 428       * @param int $b
 429       * @param int $c
 430       * @param int $d
 431       */
 432      protected static function quarterRound(&$a, &$b, &$c, &$d)
 433      {
 434          $b ^= self::leftRotate($a + $d, 7);
 435          $c ^= self::leftRotate($b + $a, 9);
 436          $d ^= self::leftRotate($c + $b, 13);
 437          $a ^= self::leftRotate($d + $c, 18);
 438      }
 439  
 440      /**
 441       * The doubleround function
 442       *
 443       * @param int $x0 (by reference)
 444       * @param int $x1 (by reference)
 445       * @param int $x2 (by reference)
 446       * @param int $x3 (by reference)
 447       * @param int $x4 (by reference)
 448       * @param int $x5 (by reference)
 449       * @param int $x6 (by reference)
 450       * @param int $x7 (by reference)
 451       * @param int $x8 (by reference)
 452       * @param int $x9 (by reference)
 453       * @param int $x10 (by reference)
 454       * @param int $x11 (by reference)
 455       * @param int $x12 (by reference)
 456       * @param int $x13 (by reference)
 457       * @param int $x14 (by reference)
 458       * @param int $x15 (by reference)
 459       */
 460      protected static function doubleRound(&$x0, &$x1, &$x2, &$x3, &$x4, &$x5, &$x6, &$x7, &$x8, &$x9, &$x10, &$x11, &$x12, &$x13, &$x14, &$x15)
 461      {
 462          // columnRound
 463          static::quarterRound($x0, $x4, $x8, $x12);
 464          static::quarterRound($x5, $x9, $x13, $x1);
 465          static::quarterRound($x10, $x14, $x2, $x6);
 466          static::quarterRound($x15, $x3, $x7, $x11);
 467          // rowRound
 468          static::quarterRound($x0, $x1, $x2, $x3);
 469          static::quarterRound($x5, $x6, $x7, $x4);
 470          static::quarterRound($x10, $x11, $x8, $x9);
 471          static::quarterRound($x15, $x12, $x13, $x14);
 472      }
 473  
 474      /**
 475       * The Salsa20 hash function function
 476       *
 477       * @param string $x
 478       */
 479      protected static function salsa20($x)
 480      {
 481          $z = $x = unpack('V*', $x);
 482          for ($i = 0; $i < 10; $i++) {
 483              static::doubleRound($z[1], $z[2], $z[3], $z[4], $z[5], $z[6], $z[7], $z[8], $z[9], $z[10], $z[11], $z[12], $z[13], $z[14], $z[15], $z[16]);
 484          }
 485  
 486          for ($i = 1; $i <= 16; $i++) {
 487              $x[$i] += $z[$i];
 488          }
 489  
 490          return pack('V*', ...$x);
 491      }
 492  
 493      /**
 494       * Calculates Poly1305 MAC
 495       *
 496       * @see self::decrypt()
 497       * @see self::encrypt()
 498       * @access private
 499       * @param string $ciphertext
 500       * @return string
 501       */
 502      protected function poly1305($ciphertext)
 503      {
 504          if (!$this->usingGeneratedPoly1305Key) {
 505              return parent::poly1305($this->aad . $ciphertext);
 506          } else {
 507              /*
 508              sodium_crypto_aead_chacha20poly1305_encrypt does not calculate the poly1305 tag
 509              the same way sodium_crypto_aead_chacha20poly1305_ietf_encrypt does. you can see
 510              how the latter encrypts it in Salsa20::encrypt(). here's how the former encrypts
 511              it:
 512  
 513              $this->newtag = $this->poly1305(
 514                  $this->aad .
 515                  pack('V', strlen($this->aad)) . "\0\0\0\0" .
 516                  $ciphertext .
 517                  pack('V', strlen($ciphertext)) . "\0\0\0\0"
 518              );
 519  
 520              phpseclib opts to use the IETF construction, even when the nonce is 64-bits
 521              instead of 96-bits
 522              */
 523              return parent::poly1305(
 524                  self::nullPad128($this->aad) .
 525                  self::nullPad128($ciphertext) .
 526                  pack('V', strlen($this->aad)) . "\0\0\0\0" .
 527                  pack('V', strlen($ciphertext)) . "\0\0\0\0"
 528              );
 529          }
 530      }
 531  }


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