[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/sodium_compat/src/ -> Crypto.php (source)

   1  <?php
   2  
   3  if (class_exists('ParagonIE_Sodium_Crypto', false)) {
   4      return;
   5  }
   6  
   7  /**
   8   * Class ParagonIE_Sodium_Crypto
   9   *
  10   * ATTENTION!
  11   *
  12   * If you are using this library, you should be using
  13   * ParagonIE_Sodium_Compat in your code, not this class.
  14   */
  15  abstract class ParagonIE_Sodium_Crypto
  16  {
  17      const aead_chacha20poly1305_KEYBYTES = 32;
  18      const aead_chacha20poly1305_NSECBYTES = 0;
  19      const aead_chacha20poly1305_NPUBBYTES = 8;
  20      const aead_chacha20poly1305_ABYTES = 16;
  21  
  22      const aead_chacha20poly1305_IETF_KEYBYTES = 32;
  23      const aead_chacha20poly1305_IETF_NSECBYTES = 0;
  24      const aead_chacha20poly1305_IETF_NPUBBYTES = 12;
  25      const aead_chacha20poly1305_IETF_ABYTES = 16;
  26  
  27      const aead_xchacha20poly1305_IETF_KEYBYTES = 32;
  28      const aead_xchacha20poly1305_IETF_NSECBYTES = 0;
  29      const aead_xchacha20poly1305_IETF_NPUBBYTES = 24;
  30      const aead_xchacha20poly1305_IETF_ABYTES = 16;
  31  
  32      const box_curve25519xsalsa20poly1305_SEEDBYTES = 32;
  33      const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32;
  34      const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32;
  35      const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32;
  36      const box_curve25519xsalsa20poly1305_NONCEBYTES = 24;
  37      const box_curve25519xsalsa20poly1305_MACBYTES = 16;
  38      const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16;
  39      const box_curve25519xsalsa20poly1305_ZEROBYTES = 32;
  40  
  41      const onetimeauth_poly1305_BYTES = 16;
  42      const onetimeauth_poly1305_KEYBYTES = 32;
  43  
  44      const secretbox_xsalsa20poly1305_KEYBYTES = 32;
  45      const secretbox_xsalsa20poly1305_NONCEBYTES = 24;
  46      const secretbox_xsalsa20poly1305_MACBYTES = 16;
  47      const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16;
  48      const secretbox_xsalsa20poly1305_ZEROBYTES = 32;
  49  
  50      const secretbox_xchacha20poly1305_KEYBYTES = 32;
  51      const secretbox_xchacha20poly1305_NONCEBYTES = 24;
  52      const secretbox_xchacha20poly1305_MACBYTES = 16;
  53      const secretbox_xchacha20poly1305_BOXZEROBYTES = 16;
  54      const secretbox_xchacha20poly1305_ZEROBYTES = 32;
  55  
  56      const stream_salsa20_KEYBYTES = 32;
  57  
  58      /**
  59       * AEAD Decryption with ChaCha20-Poly1305
  60       *
  61       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
  62       *
  63       * @param string $message
  64       * @param string $ad
  65       * @param string $nonce
  66       * @param string $key
  67       * @return string
  68       * @throws SodiumException
  69       * @throws TypeError
  70       */
  71      public static function aead_chacha20poly1305_decrypt(
  72          $message = '',
  73          $ad = '',
  74          $nonce = '',
  75          $key = ''
  76      ) {
  77          /** @var int $len - Length of message (ciphertext + MAC) */
  78          $len = ParagonIE_Sodium_Core_Util::strlen($message);
  79  
  80          /** @var int  $clen - Length of ciphertext */
  81          $clen = $len - self::aead_chacha20poly1305_ABYTES;
  82  
  83          /** @var int $adlen - Length of associated data */
  84          $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
  85  
  86          /** @var string $mac - Message authentication code */
  87          $mac = ParagonIE_Sodium_Core_Util::substr(
  88              $message,
  89              $clen,
  90              self::aead_chacha20poly1305_ABYTES
  91          );
  92  
  93          /** @var string $ciphertext - The encrypted message (sans MAC) */
  94          $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen);
  95  
  96          /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
  97          $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
  98              32,
  99              $nonce,
 100              $key
 101          );
 102  
 103          /* Recalculate the Poly1305 authentication tag (MAC): */
 104          $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
 105          try {
 106              ParagonIE_Sodium_Compat::memzero($block0);
 107          } catch (SodiumException $ex) {
 108              $block0 = null;
 109          }
 110          $state->update($ad);
 111          $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
 112          $state->update($ciphertext);
 113          $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
 114          $computed_mac = $state->finish();
 115  
 116          /* Compare the given MAC with the recalculated MAC: */
 117          if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
 118              throw new SodiumException('Invalid MAC');
 119          }
 120  
 121          // Here, we know that the MAC is valid, so we decrypt and return the plaintext
 122          return ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
 123              $ciphertext,
 124              $nonce,
 125              $key,
 126              ParagonIE_Sodium_Core_Util::store64_le(1)
 127          );
 128      }
 129  
 130      /**
 131       * AEAD Encryption with ChaCha20-Poly1305
 132       *
 133       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 134       *
 135       * @param string $message
 136       * @param string $ad
 137       * @param string $nonce
 138       * @param string $key
 139       * @return string
 140       * @throws SodiumException
 141       * @throws TypeError
 142       */
 143      public static function aead_chacha20poly1305_encrypt(
 144          $message = '',
 145          $ad = '',
 146          $nonce = '',
 147          $key = ''
 148      ) {
 149          /** @var int $len - Length of the plaintext message */
 150          $len = ParagonIE_Sodium_Core_Util::strlen($message);
 151  
 152          /** @var int $adlen - Length of the associated data */
 153          $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
 154  
 155          /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
 156          $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
 157              32,
 158              $nonce,
 159              $key
 160          );
 161          $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
 162          try {
 163              ParagonIE_Sodium_Compat::memzero($block0);
 164          } catch (SodiumException $ex) {
 165              $block0 = null;
 166          }
 167  
 168          /** @var string $ciphertext - Raw encrypted data */
 169          $ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
 170              $message,
 171              $nonce,
 172              $key,
 173              ParagonIE_Sodium_Core_Util::store64_le(1)
 174          );
 175  
 176          $state->update($ad);
 177          $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
 178          $state->update($ciphertext);
 179          $state->update(ParagonIE_Sodium_Core_Util::store64_le($len));
 180          return $ciphertext . $state->finish();
 181      }
 182  
 183      /**
 184       * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
 185       *
 186       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 187       *
 188       * @param string $message
 189       * @param string $ad
 190       * @param string $nonce
 191       * @param string $key
 192       * @return string
 193       * @throws SodiumException
 194       * @throws TypeError
 195       */
 196      public static function aead_chacha20poly1305_ietf_decrypt(
 197          $message = '',
 198          $ad = '',
 199          $nonce = '',
 200          $key = ''
 201      ) {
 202          /** @var int $adlen - Length of associated data */
 203          $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
 204  
 205          /** @var int $len - Length of message (ciphertext + MAC) */
 206          $len = ParagonIE_Sodium_Core_Util::strlen($message);
 207  
 208          /** @var int  $clen - Length of ciphertext */
 209          $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES;
 210  
 211          /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
 212          $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(
 213              32,
 214              $nonce,
 215              $key
 216          );
 217  
 218          /** @var string $mac - Message authentication code */
 219          $mac = ParagonIE_Sodium_Core_Util::substr(
 220              $message,
 221              $len - self::aead_chacha20poly1305_IETF_ABYTES,
 222              self::aead_chacha20poly1305_IETF_ABYTES
 223          );
 224  
 225          /** @var string $ciphertext - The encrypted message (sans MAC) */
 226          $ciphertext = ParagonIE_Sodium_Core_Util::substr(
 227              $message,
 228              0,
 229              $len - self::aead_chacha20poly1305_IETF_ABYTES
 230          );
 231  
 232          /* Recalculate the Poly1305 authentication tag (MAC): */
 233          $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
 234          try {
 235              ParagonIE_Sodium_Compat::memzero($block0);
 236          } catch (SodiumException $ex) {
 237              $block0 = null;
 238          }
 239          $state->update($ad);
 240          $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
 241          $state->update($ciphertext);
 242          $state->update(str_repeat("\x00", (0x10 - $clen) & 0xf));
 243          $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
 244          $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
 245          $computed_mac = $state->finish();
 246  
 247          /* Compare the given MAC with the recalculated MAC: */
 248          if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
 249              throw new SodiumException('Invalid MAC');
 250          }
 251  
 252          // Here, we know that the MAC is valid, so we decrypt and return the plaintext
 253          return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
 254              $ciphertext,
 255              $nonce,
 256              $key,
 257              ParagonIE_Sodium_Core_Util::store64_le(1)
 258          );
 259      }
 260  
 261      /**
 262       * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
 263       *
 264       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 265       *
 266       * @param string $message
 267       * @param string $ad
 268       * @param string $nonce
 269       * @param string $key
 270       * @return string
 271       * @throws SodiumException
 272       * @throws TypeError
 273       */
 274      public static function aead_chacha20poly1305_ietf_encrypt(
 275          $message = '',
 276          $ad = '',
 277          $nonce = '',
 278          $key = ''
 279      ) {
 280          /** @var int $len - Length of the plaintext message */
 281          $len = ParagonIE_Sodium_Core_Util::strlen($message);
 282  
 283          /** @var int $adlen - Length of the associated data */
 284          $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);
 285  
 286          /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
 287          $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(
 288              32,
 289              $nonce,
 290              $key
 291          );
 292          $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
 293          try {
 294              ParagonIE_Sodium_Compat::memzero($block0);
 295          } catch (SodiumException $ex) {
 296              $block0 = null;
 297          }
 298  
 299          /** @var string $ciphertext - Raw encrypted data */
 300          $ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
 301              $message,
 302              $nonce,
 303              $key,
 304              ParagonIE_Sodium_Core_Util::store64_le(1)
 305          );
 306  
 307          $state->update($ad);
 308          $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf)));
 309          $state->update($ciphertext);
 310          $state->update(str_repeat("\x00", ((0x10 - $len) & 0xf)));
 311          $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
 312          $state->update(ParagonIE_Sodium_Core_Util::store64_le($len));
 313          return $ciphertext . $state->finish();
 314      }
 315  
 316      /**
 317       * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
 318       *
 319       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 320       *
 321       * @param string $message
 322       * @param string $ad
 323       * @param string $nonce
 324       * @param string $key
 325       * @return string
 326       * @throws SodiumException
 327       * @throws TypeError
 328       */
 329      public static function aead_xchacha20poly1305_ietf_decrypt(
 330          $message = '',
 331          $ad = '',
 332          $nonce = '',
 333          $key = ''
 334      ) {
 335          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
 336              ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
 337              $key
 338          );
 339          $nonceLast = "\x00\x00\x00\x00" .
 340              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
 341  
 342          return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey);
 343      }
 344  
 345      /**
 346       * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
 347       *
 348       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 349       *
 350       * @param string $message
 351       * @param string $ad
 352       * @param string $nonce
 353       * @param string $key
 354       * @return string
 355       * @throws SodiumException
 356       * @throws TypeError
 357       */
 358      public static function aead_xchacha20poly1305_ietf_encrypt(
 359          $message = '',
 360          $ad = '',
 361          $nonce = '',
 362          $key = ''
 363      ) {
 364          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
 365              ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
 366              $key
 367          );
 368          $nonceLast = "\x00\x00\x00\x00" .
 369              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
 370  
 371          return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey);
 372      }
 373  
 374      /**
 375       * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512)
 376       *
 377       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 378       *
 379       * @param string $message
 380       * @param string $key
 381       * @return string
 382       * @throws TypeError
 383       */
 384      public static function auth($message, $key)
 385      {
 386          return ParagonIE_Sodium_Core_Util::substr(
 387              hash_hmac('sha512', $message, $key, true),
 388              0,
 389              32
 390          );
 391      }
 392  
 393      /**
 394       * HMAC-SHA-512-256 validation. Constant-time via hash_equals().
 395       *
 396       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 397       *
 398       * @param string $mac
 399       * @param string $message
 400       * @param string $key
 401       * @return bool
 402       * @throws SodiumException
 403       * @throws TypeError
 404       */
 405      public static function auth_verify($mac, $message, $key)
 406      {
 407          return ParagonIE_Sodium_Core_Util::hashEquals(
 408              $mac,
 409              self::auth($message, $key)
 410          );
 411      }
 412  
 413      /**
 414       * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption
 415       *
 416       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 417       *
 418       * @param string $plaintext
 419       * @param string $nonce
 420       * @param string $keypair
 421       * @return string
 422       * @throws SodiumException
 423       * @throws TypeError
 424       */
 425      public static function box($plaintext, $nonce, $keypair)
 426      {
 427          $c = self::secretbox(
 428              $plaintext,
 429              $nonce,
 430              self::box_beforenm(
 431                  self::box_secretkey($keypair),
 432                  self::box_publickey($keypair)
 433              )
 434          );
 435          return $c;
 436      }
 437  
 438      /**
 439       * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
 440       *
 441       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 442       *
 443       * @param string $message
 444       * @param string $publicKey
 445       * @return string
 446       * @throws SodiumException
 447       * @throws TypeError
 448       */
 449      public static function box_seal($message, $publicKey)
 450      {
 451          /** @var string $ephemeralKeypair */
 452          $ephemeralKeypair = self::box_keypair();
 453  
 454          /** @var string $ephemeralSK */
 455          $ephemeralSK = self::box_secretkey($ephemeralKeypair);
 456  
 457          /** @var string $ephemeralPK */
 458          $ephemeralPK = self::box_publickey($ephemeralKeypair);
 459  
 460          /** @var string $nonce */
 461          $nonce = self::generichash(
 462              $ephemeralPK . $publicKey,
 463              '',
 464              24
 465          );
 466  
 467          /** @var string $keypair - The combined keypair used in crypto_box() */
 468          $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
 469  
 470          /** @var string $ciphertext Ciphertext + MAC from crypto_box */
 471          $ciphertext = self::box($message, $nonce, $keypair);
 472          try {
 473              ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
 474              ParagonIE_Sodium_Compat::memzero($ephemeralSK);
 475              ParagonIE_Sodium_Compat::memzero($nonce);
 476          } catch (SodiumException $ex) {
 477              $ephemeralKeypair = null;
 478              $ephemeralSK = null;
 479              $nonce = null;
 480          }
 481          return $ephemeralPK . $ciphertext;
 482      }
 483  
 484      /**
 485       * Opens a message encrypted via box_seal().
 486       *
 487       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 488       *
 489       * @param string $message
 490       * @param string $keypair
 491       * @return string
 492       * @throws SodiumException
 493       * @throws TypeError
 494       */
 495      public static function box_seal_open($message, $keypair)
 496      {
 497          /** @var string $ephemeralPK */
 498          $ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32);
 499  
 500          /** @var string $ciphertext (ciphertext + MAC) */
 501          $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32);
 502  
 503          /** @var string $secretKey */
 504          $secretKey = self::box_secretkey($keypair);
 505  
 506          /** @var string $publicKey */
 507          $publicKey = self::box_publickey($keypair);
 508  
 509          /** @var string $nonce */
 510          $nonce = self::generichash(
 511              $ephemeralPK . $publicKey,
 512              '',
 513              24
 514          );
 515  
 516          /** @var string $keypair */
 517          $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
 518  
 519          /** @var string $m */
 520          $m = self::box_open($ciphertext, $nonce, $keypair);
 521          try {
 522              ParagonIE_Sodium_Compat::memzero($secretKey);
 523              ParagonIE_Sodium_Compat::memzero($ephemeralPK);
 524              ParagonIE_Sodium_Compat::memzero($nonce);
 525          } catch (SodiumException $ex) {
 526              $secretKey = null;
 527              $ephemeralPK = null;
 528              $nonce = null;
 529          }
 530          return $m;
 531      }
 532  
 533      /**
 534       * Used by crypto_box() to get the crypto_secretbox() key.
 535       *
 536       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 537       *
 538       * @param string $sk
 539       * @param string $pk
 540       * @return string
 541       * @throws SodiumException
 542       * @throws TypeError
 543       */
 544      public static function box_beforenm($sk, $pk)
 545      {
 546          return ParagonIE_Sodium_Core_HSalsa20::hsalsa20(
 547              str_repeat("\x00", 16),
 548              self::scalarmult($sk, $pk)
 549          );
 550      }
 551  
 552      /**
 553       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 554       *
 555       * @return string
 556       * @throws Exception
 557       * @throws SodiumException
 558       * @throws TypeError
 559       */
 560      public static function box_keypair()
 561      {
 562          $sKey = random_bytes(32);
 563          $pKey = self::scalarmult_base($sKey);
 564          return $sKey . $pKey;
 565      }
 566  
 567      /**
 568       * @param string $seed
 569       * @return string
 570       * @throws SodiumException
 571       * @throws TypeError
 572       */
 573      public static function box_seed_keypair($seed)
 574      {
 575          $sKey = ParagonIE_Sodium_Core_Util::substr(
 576              hash('sha512', $seed, true),
 577              0,
 578              32
 579          );
 580          $pKey = self::scalarmult_base($sKey);
 581          return $sKey . $pKey;
 582      }
 583  
 584      /**
 585       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 586       *
 587       * @param string $sKey
 588       * @param string $pKey
 589       * @return string
 590       * @throws TypeError
 591       */
 592      public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
 593      {
 594          return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) .
 595              ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32);
 596      }
 597  
 598      /**
 599       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 600       *
 601       * @param string $keypair
 602       * @return string
 603       * @throws RangeException
 604       * @throws TypeError
 605       */
 606      public static function box_secretkey($keypair)
 607      {
 608          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) {
 609              throw new RangeException(
 610                  'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
 611              );
 612          }
 613          return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32);
 614      }
 615  
 616      /**
 617       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 618       *
 619       * @param string $keypair
 620       * @return string
 621       * @throws RangeException
 622       * @throws TypeError
 623       */
 624      public static function box_publickey($keypair)
 625      {
 626          if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
 627              throw new RangeException(
 628                  'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
 629              );
 630          }
 631          return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32);
 632      }
 633  
 634      /**
 635       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 636       *
 637       * @param string $sKey
 638       * @return string
 639       * @throws RangeException
 640       * @throws SodiumException
 641       * @throws TypeError
 642       */
 643      public static function box_publickey_from_secretkey($sKey)
 644      {
 645          if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
 646              throw new RangeException(
 647                  'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
 648              );
 649          }
 650          return self::scalarmult_base($sKey);
 651      }
 652  
 653      /**
 654       * Decrypt a message encrypted with box().
 655       *
 656       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 657       *
 658       * @param string $ciphertext
 659       * @param string $nonce
 660       * @param string $keypair
 661       * @return string
 662       * @throws SodiumException
 663       * @throws TypeError
 664       */
 665      public static function box_open($ciphertext, $nonce, $keypair)
 666      {
 667          return self::secretbox_open(
 668              $ciphertext,
 669              $nonce,
 670              self::box_beforenm(
 671                  self::box_secretkey($keypair),
 672                  self::box_publickey($keypair)
 673              )
 674          );
 675      }
 676  
 677      /**
 678       * Calculate a BLAKE2b hash.
 679       *
 680       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 681       *
 682       * @param string $message
 683       * @param string|null $key
 684       * @param int $outlen
 685       * @return string
 686       * @throws RangeException
 687       * @throws SodiumException
 688       * @throws TypeError
 689       */
 690      public static function generichash($message, $key = '', $outlen = 32)
 691      {
 692          // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
 693          ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
 694  
 695          $k = null;
 696          if (!empty($key)) {
 697              /** @var SplFixedArray $k */
 698              $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
 699              if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
 700                  throw new RangeException('Invalid key size');
 701              }
 702          }
 703  
 704          /** @var SplFixedArray $in */
 705          $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
 706  
 707          /** @var SplFixedArray $ctx */
 708          $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen);
 709          ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count());
 710  
 711          /** @var SplFixedArray $out */
 712          $out = new SplFixedArray($outlen);
 713          $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out);
 714  
 715          /** @var array<int, int> */
 716          $outArray = $out->toArray();
 717          return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
 718      }
 719  
 720      /**
 721       * Finalize a BLAKE2b hashing context, returning the hash.
 722       *
 723       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 724       *
 725       * @param string $ctx
 726       * @param int $outlen
 727       * @return string
 728       * @throws SodiumException
 729       * @throws TypeError
 730       */
 731      public static function generichash_final($ctx, $outlen = 32)
 732      {
 733          if (!is_string($ctx)) {
 734              throw new TypeError('Context must be a string');
 735          }
 736          $out = new SplFixedArray($outlen);
 737  
 738          /** @var SplFixedArray $context */
 739          $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
 740  
 741          /** @var SplFixedArray $out */
 742          $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out);
 743  
 744          /** @var array<int, int> */
 745          $outArray = $out->toArray();
 746          return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
 747      }
 748  
 749      /**
 750       * Initialize a hashing context for BLAKE2b.
 751       *
 752       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 753       *
 754       * @param string $key
 755       * @param int $outputLength
 756       * @return string
 757       * @throws RangeException
 758       * @throws SodiumException
 759       * @throws TypeError
 760       */
 761      public static function generichash_init($key = '', $outputLength = 32)
 762      {
 763          // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
 764          ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
 765  
 766          $k = null;
 767          if (!empty($key)) {
 768              $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
 769              if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
 770                  throw new RangeException('Invalid key size');
 771              }
 772          }
 773  
 774          /** @var SplFixedArray $ctx */
 775          $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength);
 776  
 777          return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
 778      }
 779  
 780      /**
 781       * Initialize a hashing context for BLAKE2b.
 782       *
 783       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 784       *
 785       * @param string $key
 786       * @param int $outputLength
 787       * @param string $salt
 788       * @param string $personal
 789       * @return string
 790       * @throws RangeException
 791       * @throws SodiumException
 792       * @throws TypeError
 793       */
 794      public static function generichash_init_salt_personal(
 795          $key = '',
 796          $outputLength = 32,
 797          $salt = '',
 798          $personal = ''
 799      ) {
 800          // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
 801          ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
 802  
 803          $k = null;
 804          if (!empty($key)) {
 805              $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
 806              if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
 807                  throw new RangeException('Invalid key size');
 808              }
 809          }
 810          if (!empty($salt)) {
 811              $s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt);
 812          } else {
 813              $s = null;
 814          }
 815          if (!empty($salt)) {
 816              $p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal);
 817          } else {
 818              $p = null;
 819          }
 820  
 821          /** @var SplFixedArray $ctx */
 822          $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p);
 823  
 824          return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
 825      }
 826  
 827      /**
 828       * Update a hashing context for BLAKE2b with $message
 829       *
 830       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 831       *
 832       * @param string $ctx
 833       * @param string $message
 834       * @return string
 835       * @throws SodiumException
 836       * @throws TypeError
 837       */
 838      public static function generichash_update($ctx, $message)
 839      {
 840          // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
 841          ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
 842  
 843          /** @var SplFixedArray $context */
 844          $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
 845  
 846          /** @var SplFixedArray $in */
 847          $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
 848  
 849          ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count());
 850  
 851          return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context);
 852      }
 853  
 854      /**
 855       * Libsodium's crypto_kx().
 856       *
 857       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 858       *
 859       * @param string $my_sk
 860       * @param string $their_pk
 861       * @param string $client_pk
 862       * @param string $server_pk
 863       * @return string
 864       * @throws SodiumException
 865       * @throws TypeError
 866       */
 867      public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
 868      {
 869          return ParagonIE_Sodium_Compat::crypto_generichash(
 870              ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) .
 871              $client_pk .
 872              $server_pk
 873          );
 874      }
 875  
 876      /**
 877       * ECDH over Curve25519
 878       *
 879       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 880       *
 881       * @param string $sKey
 882       * @param string $pKey
 883       * @return string
 884       *
 885       * @throws SodiumException
 886       * @throws TypeError
 887       */
 888      public static function scalarmult($sKey, $pKey)
 889      {
 890          $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
 891          self::scalarmult_throw_if_zero($q);
 892          return $q;
 893      }
 894  
 895      /**
 896       * ECDH over Curve25519, using the basepoint.
 897       * Used to get a secret key from a public key.
 898       *
 899       * @param string $secret
 900       * @return string
 901       *
 902       * @throws SodiumException
 903       * @throws TypeError
 904       */
 905      public static function scalarmult_base($secret)
 906      {
 907          $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
 908          self::scalarmult_throw_if_zero($q);
 909          return $q;
 910      }
 911  
 912      /**
 913       * This throws an Error if a zero public key was passed to the function.
 914       *
 915       * @param string $q
 916       * @return void
 917       * @throws SodiumException
 918       * @throws TypeError
 919       */
 920      protected static function scalarmult_throw_if_zero($q)
 921      {
 922          $d = 0;
 923          for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
 924              $d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]);
 925          }
 926  
 927          /* branch-free variant of === 0 */
 928          if (-(1 & (($d - 1) >> 8))) {
 929              throw new SodiumException('Zero public key is not allowed');
 930          }
 931      }
 932  
 933      /**
 934       * XSalsa20-Poly1305 authenticated symmetric-key encryption.
 935       *
 936       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 937       *
 938       * @param string $plaintext
 939       * @param string $nonce
 940       * @param string $key
 941       * @return string
 942       * @throws SodiumException
 943       * @throws TypeError
 944       */
 945      public static function secretbox($plaintext, $nonce, $key)
 946      {
 947          /** @var string $subkey */
 948          $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
 949  
 950          /** @var string $block0 */
 951          $block0 = str_repeat("\x00", 32);
 952  
 953          /** @var int $mlen - Length of the plaintext message */
 954          $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
 955          $mlen0 = $mlen;
 956          if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
 957              $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
 958          }
 959          $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
 960  
 961          /** @var string $block0 */
 962          $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor(
 963              $block0,
 964              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
 965              $subkey
 966          );
 967  
 968          /** @var string $c */
 969          $c = ParagonIE_Sodium_Core_Util::substr(
 970              $block0,
 971              self::secretbox_xsalsa20poly1305_ZEROBYTES
 972          );
 973          if ($mlen > $mlen0) {
 974              $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
 975                  ParagonIE_Sodium_Core_Util::substr(
 976                      $plaintext,
 977                      self::secretbox_xsalsa20poly1305_ZEROBYTES
 978                  ),
 979                  ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
 980                  1,
 981                  $subkey
 982              );
 983          }
 984          $state = new ParagonIE_Sodium_Core_Poly1305_State(
 985              ParagonIE_Sodium_Core_Util::substr(
 986                  $block0,
 987                  0,
 988                  self::onetimeauth_poly1305_KEYBYTES
 989              )
 990          );
 991          try {
 992              ParagonIE_Sodium_Compat::memzero($block0);
 993              ParagonIE_Sodium_Compat::memzero($subkey);
 994          } catch (SodiumException $ex) {
 995              $block0 = null;
 996              $subkey = null;
 997          }
 998  
 999          $state->update($c);
1000  
1001          /** @var string $c - MAC || ciphertext */
1002          $c = $state->finish() . $c;
1003          unset($state);
1004  
1005          return $c;
1006      }
1007  
1008      /**
1009       * Decrypt a ciphertext generated via secretbox().
1010       *
1011       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1012       *
1013       * @param string $ciphertext
1014       * @param string $nonce
1015       * @param string $key
1016       * @return string
1017       * @throws SodiumException
1018       * @throws TypeError
1019       */
1020      public static function secretbox_open($ciphertext, $nonce, $key)
1021      {
1022          /** @var string $mac */
1023          $mac = ParagonIE_Sodium_Core_Util::substr(
1024              $ciphertext,
1025              0,
1026              self::secretbox_xsalsa20poly1305_MACBYTES
1027          );
1028  
1029          /** @var string $c */
1030          $c = ParagonIE_Sodium_Core_Util::substr(
1031              $ciphertext,
1032              self::secretbox_xsalsa20poly1305_MACBYTES
1033          );
1034  
1035          /** @var int $clen */
1036          $clen = ParagonIE_Sodium_Core_Util::strlen($c);
1037  
1038          /** @var string $subkey */
1039          $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
1040  
1041          /** @var string $block0 */
1042          $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
1043              64,
1044              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1045              $subkey
1046          );
1047          $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
1048              $mac,
1049              $c,
1050              ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
1051          );
1052          if (!$verified) {
1053              try {
1054                  ParagonIE_Sodium_Compat::memzero($subkey);
1055              } catch (SodiumException $ex) {
1056                  $subkey = null;
1057              }
1058              throw new SodiumException('Invalid MAC');
1059          }
1060  
1061          /** @var string $m - Decrypted message */
1062          $m = ParagonIE_Sodium_Core_Util::xorStrings(
1063              ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
1064              ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
1065          );
1066          if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
1067              // We had more than 1 block, so let's continue to decrypt the rest.
1068              $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
1069                  ParagonIE_Sodium_Core_Util::substr(
1070                      $c,
1071                      self::secretbox_xsalsa20poly1305_ZEROBYTES
1072                  ),
1073                  ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1074                  1,
1075                  (string) $subkey
1076              );
1077          }
1078          return $m;
1079      }
1080  
1081      /**
1082       * XChaCha20-Poly1305 authenticated symmetric-key encryption.
1083       *
1084       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1085       *
1086       * @param string $plaintext
1087       * @param string $nonce
1088       * @param string $key
1089       * @return string
1090       * @throws SodiumException
1091       * @throws TypeError
1092       */
1093      public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
1094      {
1095          /** @var string $subkey */
1096          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1097              ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
1098              $key
1099          );
1100          $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
1101  
1102          /** @var string $block0 */
1103          $block0 = str_repeat("\x00", 32);
1104  
1105          /** @var int $mlen - Length of the plaintext message */
1106          $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
1107          $mlen0 = $mlen;
1108          if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
1109              $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
1110          }
1111          $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
1112  
1113          /** @var string $block0 */
1114          $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1115              $block0,
1116              $nonceLast,
1117              $subkey
1118          );
1119  
1120          /** @var string $c */
1121          $c = ParagonIE_Sodium_Core_Util::substr(
1122              $block0,
1123              self::secretbox_xchacha20poly1305_ZEROBYTES
1124          );
1125          if ($mlen > $mlen0) {
1126              $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1127                  ParagonIE_Sodium_Core_Util::substr(
1128                      $plaintext,
1129                      self::secretbox_xchacha20poly1305_ZEROBYTES
1130                  ),
1131                  $nonceLast,
1132                  $subkey,
1133                  ParagonIE_Sodium_Core_Util::store64_le(1)
1134              );
1135          }
1136          $state = new ParagonIE_Sodium_Core_Poly1305_State(
1137              ParagonIE_Sodium_Core_Util::substr(
1138                  $block0,
1139                  0,
1140                  self::onetimeauth_poly1305_KEYBYTES
1141              )
1142          );
1143          try {
1144              ParagonIE_Sodium_Compat::memzero($block0);
1145              ParagonIE_Sodium_Compat::memzero($subkey);
1146          } catch (SodiumException $ex) {
1147              $block0 = null;
1148              $subkey = null;
1149          }
1150  
1151          $state->update($c);
1152  
1153          /** @var string $c - MAC || ciphertext */
1154          $c = $state->finish() . $c;
1155          unset($state);
1156  
1157          return $c;
1158      }
1159  
1160      /**
1161       * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
1162       *
1163       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1164       *
1165       * @param string $ciphertext
1166       * @param string $nonce
1167       * @param string $key
1168       * @return string
1169       * @throws SodiumException
1170       * @throws TypeError
1171       */
1172      public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
1173      {
1174          /** @var string $mac */
1175          $mac = ParagonIE_Sodium_Core_Util::substr(
1176              $ciphertext,
1177              0,
1178              self::secretbox_xchacha20poly1305_MACBYTES
1179          );
1180  
1181          /** @var string $c */
1182          $c = ParagonIE_Sodium_Core_Util::substr(
1183              $ciphertext,
1184              self::secretbox_xchacha20poly1305_MACBYTES
1185          );
1186  
1187          /** @var int $clen */
1188          $clen = ParagonIE_Sodium_Core_Util::strlen($c);
1189  
1190          /** @var string $subkey */
1191          $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20(
1192              ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
1193              $key
1194          );
1195  
1196          /** @var string $block0 */
1197          $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
1198              64,
1199              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1200              $subkey
1201          );
1202          $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
1203              $mac,
1204              $c,
1205              ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
1206          );
1207  
1208          if (!$verified) {
1209              try {
1210                  ParagonIE_Sodium_Compat::memzero($subkey);
1211              } catch (SodiumException $ex) {
1212                  $subkey = null;
1213              }
1214              throw new SodiumException('Invalid MAC');
1215          }
1216  
1217          /** @var string $m - Decrypted message */
1218          $m = ParagonIE_Sodium_Core_Util::xorStrings(
1219              ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
1220              ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
1221          );
1222  
1223          if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
1224              // We had more than 1 block, so let's continue to decrypt the rest.
1225              $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1226                  ParagonIE_Sodium_Core_Util::substr(
1227                      $c,
1228                      self::secretbox_xchacha20poly1305_ZEROBYTES
1229                  ),
1230                  ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1231                  (string) $subkey,
1232                  ParagonIE_Sodium_Core_Util::store64_le(1)
1233              );
1234          }
1235          return $m;
1236      }
1237  
1238      /**
1239       * @param string $key
1240       * @return array<int, string> Returns a state and a header.
1241       * @throws Exception
1242       * @throws SodiumException
1243       */
1244      public static function secretstream_xchacha20poly1305_init_push($key)
1245      {
1246          # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
1247          $out = random_bytes(24);
1248  
1249          # crypto_core_hchacha20(state->k, out, k, NULL);
1250          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1251              ParagonIE_Sodium_Core_Util::substr($out, 0, 16),
1252              $key
1253          );
1254          $state = new ParagonIE_Sodium_Core_SecretStream_State(
1255              $subkey,
1256              ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4)
1257          );
1258  
1259          # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1260          $state->counterReset();
1261  
1262          # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
1263          #        crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1264          # memset(state->_pad, 0, sizeof state->_pad);
1265          return array(
1266              $state->toString(),
1267              $out
1268          );
1269      }
1270  
1271      /**
1272       * @param string $key
1273       * @param string $header
1274       * @return string Returns a state.
1275       * @throws Exception
1276       */
1277      public static function secretstream_xchacha20poly1305_init_pull($key, $header)
1278      {
1279          # crypto_core_hchacha20(state->k, in, k, NULL);
1280          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1281              ParagonIE_Sodium_Core_Util::substr($header, 0, 16),
1282              $key
1283          );
1284          $state = new ParagonIE_Sodium_Core_SecretStream_State(
1285              $subkey,
1286              ParagonIE_Sodium_Core_Util::substr($header, 16)
1287          );
1288          $state->counterReset();
1289          # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
1290          #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1291          # memset(state->_pad, 0, sizeof state->_pad);
1292          # return 0;
1293          return $state->toString();
1294      }
1295  
1296      /**
1297       * @param string $state
1298       * @param string $msg
1299       * @param string $aad
1300       * @param int $tag
1301       * @return string
1302       * @throws SodiumException
1303       */
1304      public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
1305      {
1306          $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1307          # crypto_onetimeauth_poly1305_state poly1305_state;
1308          # unsigned char                     block[64U];
1309          # unsigned char                     slen[8U];
1310          # unsigned char                    *c;
1311          # unsigned char                    *mac;
1312  
1313          $msglen = ParagonIE_Sodium_Core_Util::strlen($msg);
1314          $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
1315  
1316          if ((($msglen + 63) >> 6) > 0xfffffffe) {
1317              throw new SodiumException(
1318                  'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1319              );
1320          }
1321  
1322          # if (outlen_p != NULL) {
1323          #     *outlen_p = 0U;
1324          # }
1325          # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1326          #     sodium_misuse();
1327          # }
1328  
1329          # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1330          # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1331          # sodium_memzero(block, sizeof block);
1332          $auth = new ParagonIE_Sodium_Core_Poly1305_State(
1333              ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1334          );
1335  
1336          # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1337          $auth->update($aad);
1338  
1339          # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1340          #     (0x10 - adlen) & 0xf);
1341          $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1342  
1343          # memset(block, 0, sizeof block);
1344          # block[0] = tag;
1345          # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1346          #                                    state->nonce, 1U, state->k);
1347          $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1348              ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63),
1349              $st->getCombinedNonce(),
1350              $st->getKey(),
1351              ParagonIE_Sodium_Core_Util::store64_le(1)
1352          );
1353  
1354          # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1355          $auth->update($block);
1356  
1357          # out[0] = block[0];
1358          $out = $block[0];
1359          # c = out + (sizeof tag);
1360          # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
1361          $cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1362              $msg,
1363              $st->getCombinedNonce(),
1364              $st->getKey(),
1365              ParagonIE_Sodium_Core_Util::store64_le(2)
1366          );
1367  
1368          # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1369          $auth->update($cipher);
1370  
1371          $out .= $cipher;
1372          unset($cipher);
1373  
1374          # crypto_onetimeauth_poly1305_update
1375          # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1376          $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1377  
1378          # STORE64_LE(slen, (uint64_t) adlen);
1379          $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
1380  
1381          # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1382          $auth->update($slen);
1383  
1384          # STORE64_LE(slen, (sizeof block) + mlen);
1385          $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
1386  
1387          # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1388          $auth->update($slen);
1389  
1390          # mac = c + mlen;
1391          # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1392          $mac = $auth->finish();
1393          $out .= $mac;
1394  
1395          # sodium_memzero(&poly1305_state, sizeof poly1305_state);
1396          unset($auth);
1397  
1398  
1399          # XOR_BUF(STATE_INONCE(state), mac,
1400          #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1401          $st->xorNonce($mac);
1402  
1403          # sodium_increment(STATE_COUNTER(state),
1404          #     crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1405          $st->incrementCounter();
1406          // Overwrite by reference:
1407          $state = $st->toString();
1408  
1409          /** @var bool $rekey */
1410          $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1411          # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1412          #     sodium_is_zero(STATE_COUNTER(state),
1413          #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1414          #     crypto_secretstream_xchacha20poly1305_rekey(state);
1415          # }
1416          if ($rekey || $st->needsRekey()) {
1417              // DO REKEY
1418              self::secretstream_xchacha20poly1305_rekey($state);
1419          }
1420          # if (outlen_p != NULL) {
1421          #     *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
1422          # }
1423          return $out;
1424      }
1425  
1426      /**
1427       * @param string $state
1428       * @param string $cipher
1429       * @param string $aad
1430       * @return bool|array{0: string, 1: int}
1431       * @throws SodiumException
1432       */
1433      public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
1434      {
1435          $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1436  
1437          $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher);
1438          #     mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
1439          $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
1440          $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
1441  
1442          #     if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1443          #         sodium_misuse();
1444          #     }
1445          if ((($msglen + 63) >> 6) > 0xfffffffe) {
1446              throw new SodiumException(
1447                  'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1448              );
1449          }
1450  
1451          #     crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1452          #     crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1453          #     sodium_memzero(block, sizeof block);
1454          $auth = new ParagonIE_Sodium_Core_Poly1305_State(
1455              ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1456          );
1457  
1458          #     crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1459          $auth->update($aad);
1460  
1461          #     crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1462          #         (0x10 - adlen) & 0xf);
1463          $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1464  
1465  
1466          #     memset(block, 0, sizeof block);
1467          #     block[0] = in[0];
1468          #     crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1469          #                                        state->nonce, 1U, state->k);
1470          $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1471              $cipher[0] . str_repeat("\0", 63),
1472              $st->getCombinedNonce(),
1473              $st->getKey(),
1474              ParagonIE_Sodium_Core_Util::store64_le(1)
1475          );
1476          #     tag = block[0];
1477          #     block[0] = in[0];
1478          #     crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1479          $tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]);
1480          $block[0] = $cipher[0];
1481          $auth->update($block);
1482  
1483  
1484          #     c = in + (sizeof tag);
1485          #     crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1486          $auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen));
1487  
1488          #     crypto_onetimeauth_poly1305_update
1489          #     (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1490          $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1491  
1492          #     STORE64_LE(slen, (uint64_t) adlen);
1493          #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1494          $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
1495          $auth->update($slen);
1496  
1497          #     STORE64_LE(slen, (sizeof block) + mlen);
1498          #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1499          $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
1500          $auth->update($slen);
1501  
1502          #     crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1503          #     sodium_memzero(&poly1305_state, sizeof poly1305_state);
1504          $mac = $auth->finish();
1505  
1506          #     stored_mac = c + mlen;
1507          #     if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
1508          #     sodium_memzero(mac, sizeof mac);
1509          #         return -1;
1510          #     }
1511  
1512          $stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16);
1513          if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) {
1514              return false;
1515          }
1516  
1517          #     crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
1518          $out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1519              ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen),
1520              $st->getCombinedNonce(),
1521              $st->getKey(),
1522              ParagonIE_Sodium_Core_Util::store64_le(2)
1523          );
1524  
1525          #     XOR_BUF(STATE_INONCE(state), mac,
1526          #         crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1527          $st->xorNonce($mac);
1528  
1529          #     sodium_increment(STATE_COUNTER(state),
1530          #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1531          $st->incrementCounter();
1532  
1533          #     if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1534          #         sodium_is_zero(STATE_COUNTER(state),
1535          #             crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1536          #         crypto_secretstream_xchacha20poly1305_rekey(state);
1537          #     }
1538  
1539          // Overwrite by reference:
1540          $state = $st->toString();
1541  
1542          /** @var bool $rekey */
1543          $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1544          if ($rekey || $st->needsRekey()) {
1545              // DO REKEY
1546              self::secretstream_xchacha20poly1305_rekey($state);
1547          }
1548          return array($out, $tag);
1549      }
1550  
1551      /**
1552       * @param string $state
1553       * @return void
1554       * @throws SodiumException
1555       */
1556      public static function secretstream_xchacha20poly1305_rekey(&$state)
1557      {
1558          $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1559          # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
1560          # crypto_secretstream_xchacha20poly1305_INONCEBYTES];
1561          # size_t        i;
1562          # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1563          #     new_key_and_inonce[i] = state->k[i];
1564          # }
1565          $new_key_and_inonce = $st->getKey();
1566  
1567          # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1568          #     new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
1569          #         STATE_INONCE(state)[i];
1570          # }
1571          $new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8);
1572  
1573          # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
1574          #                                 sizeof new_key_and_inonce,
1575          #                                 state->nonce, state->k);
1576  
1577          $st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1578              $new_key_and_inonce,
1579              $st->getCombinedNonce(),
1580              $st->getKey(),
1581              ParagonIE_Sodium_Core_Util::store64_le(0)
1582          ));
1583  
1584          # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1585          #     state->k[i] = new_key_and_inonce[i];
1586          # }
1587          # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1588          #     STATE_INONCE(state)[i] =
1589          #          new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
1590          # }
1591          # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1592          $st->counterReset();
1593  
1594          $state = $st->toString();
1595      }
1596  
1597      /**
1598       * Detached Ed25519 signature.
1599       *
1600       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1601       *
1602       * @param string $message
1603       * @param string $sk
1604       * @return string
1605       * @throws SodiumException
1606       * @throws TypeError
1607       */
1608      public static function sign_detached($message, $sk)
1609      {
1610          return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk);
1611      }
1612  
1613      /**
1614       * Attached Ed25519 signature. (Returns a signed message.)
1615       *
1616       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1617       *
1618       * @param string $message
1619       * @param string $sk
1620       * @return string
1621       * @throws SodiumException
1622       * @throws TypeError
1623       */
1624      public static function sign($message, $sk)
1625      {
1626          return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk);
1627      }
1628  
1629      /**
1630       * Opens a signed message. If valid, returns the message.
1631       *
1632       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1633       *
1634       * @param string $signedMessage
1635       * @param string $pk
1636       * @return string
1637       * @throws SodiumException
1638       * @throws TypeError
1639       */
1640      public static function sign_open($signedMessage, $pk)
1641      {
1642          return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk);
1643      }
1644  
1645      /**
1646       * Verify a detached signature of a given message and public key.
1647       *
1648       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1649       *
1650       * @param string $signature
1651       * @param string $message
1652       * @param string $pk
1653       * @return bool
1654       * @throws SodiumException
1655       * @throws TypeError
1656       */
1657      public static function sign_verify_detached($signature, $message, $pk)
1658      {
1659          return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk);
1660      }
1661  }


Generated : Fri Oct 10 08:20:03 2025 Cross-referenced by PHPXref