[ 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($nonce, $key);
1192  
1193          /** @var string $block0 */
1194          $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
1195              64,
1196              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1197              $subkey
1198          );
1199          $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
1200              $mac,
1201              $c,
1202              ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
1203          );
1204  
1205          if (!$verified) {
1206              try {
1207                  ParagonIE_Sodium_Compat::memzero($subkey);
1208              } catch (SodiumException $ex) {
1209                  $subkey = null;
1210              }
1211              throw new SodiumException('Invalid MAC');
1212          }
1213  
1214          /** @var string $m - Decrypted message */
1215          $m = ParagonIE_Sodium_Core_Util::xorStrings(
1216              ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
1217              ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
1218          );
1219  
1220          if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
1221              // We had more than 1 block, so let's continue to decrypt the rest.
1222              $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1223                  ParagonIE_Sodium_Core_Util::substr(
1224                      $c,
1225                      self::secretbox_xchacha20poly1305_ZEROBYTES
1226                  ),
1227                  ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1228                  (string) $subkey,
1229                  ParagonIE_Sodium_Core_Util::store64_le(1)
1230              );
1231          }
1232          return $m;
1233      }
1234  
1235      /**
1236       * @param string $key
1237       * @return array<int, string> Returns a state and a header.
1238       * @throws Exception
1239       * @throws SodiumException
1240       */
1241      public static function secretstream_xchacha20poly1305_init_push($key)
1242      {
1243          # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
1244          $out = random_bytes(24);
1245  
1246          # crypto_core_hchacha20(state->k, out, k, NULL);
1247          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key);
1248          $state = new ParagonIE_Sodium_Core_SecretStream_State(
1249              $subkey,
1250              ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4)
1251          );
1252  
1253          # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1254          $state->counterReset();
1255  
1256          # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
1257          #        crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1258          # memset(state->_pad, 0, sizeof state->_pad);
1259          return array(
1260              $state->toString(),
1261              $out
1262          );
1263      }
1264  
1265      /**
1266       * @param string $key
1267       * @param string $header
1268       * @return string Returns a state.
1269       * @throws Exception
1270       */
1271      public static function secretstream_xchacha20poly1305_init_pull($key, $header)
1272      {
1273          # crypto_core_hchacha20(state->k, in, k, NULL);
1274          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1275              ParagonIE_Sodium_Core_Util::substr($header, 0, 16),
1276              $key
1277          );
1278          $state = new ParagonIE_Sodium_Core_SecretStream_State(
1279              $subkey,
1280              ParagonIE_Sodium_Core_Util::substr($header, 16)
1281          );
1282          $state->counterReset();
1283          # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
1284          #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1285          # memset(state->_pad, 0, sizeof state->_pad);
1286          # return 0;
1287          return $state->toString();
1288      }
1289  
1290      /**
1291       * @param string $state
1292       * @param string $msg
1293       * @param string $aad
1294       * @param int $tag
1295       * @return string
1296       * @throws SodiumException
1297       */
1298      public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
1299      {
1300          $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1301          # crypto_onetimeauth_poly1305_state poly1305_state;
1302          # unsigned char                     block[64U];
1303          # unsigned char                     slen[8U];
1304          # unsigned char                    *c;
1305          # unsigned char                    *mac;
1306  
1307          $msglen = ParagonIE_Sodium_Core_Util::strlen($msg);
1308          $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
1309  
1310          if ((($msglen + 63) >> 6) > 0xfffffffe) {
1311              throw new SodiumException(
1312                  'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1313              );
1314          }
1315  
1316          # if (outlen_p != NULL) {
1317          #     *outlen_p = 0U;
1318          # }
1319          # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1320          #     sodium_misuse();
1321          # }
1322  
1323          # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1324          # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1325          # sodium_memzero(block, sizeof block);
1326          $auth = new ParagonIE_Sodium_Core_Poly1305_State(
1327              ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1328          );
1329  
1330          # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1331          $auth->update($aad);
1332  
1333          # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1334          #     (0x10 - adlen) & 0xf);
1335          $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1336  
1337          # memset(block, 0, sizeof block);
1338          # block[0] = tag;
1339          # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1340          #                                    state->nonce, 1U, state->k);
1341          $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1342              ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63),
1343              $st->getCombinedNonce(),
1344              $st->getKey(),
1345              ParagonIE_Sodium_Core_Util::store64_le(1)
1346          );
1347  
1348          # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1349          $auth->update($block);
1350  
1351          # out[0] = block[0];
1352          $out = $block[0];
1353          # c = out + (sizeof tag);
1354          # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
1355          $cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1356              $msg,
1357              $st->getCombinedNonce(),
1358              $st->getKey(),
1359              ParagonIE_Sodium_Core_Util::store64_le(2)
1360          );
1361  
1362          # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1363          $auth->update($cipher);
1364  
1365          $out .= $cipher;
1366          unset($cipher);
1367  
1368          # crypto_onetimeauth_poly1305_update
1369          # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1370          $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1371  
1372          # STORE64_LE(slen, (uint64_t) adlen);
1373          $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
1374  
1375          # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1376          $auth->update($slen);
1377  
1378          # STORE64_LE(slen, (sizeof block) + mlen);
1379          $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
1380  
1381          # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1382          $auth->update($slen);
1383  
1384          # mac = c + mlen;
1385          # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1386          $mac = $auth->finish();
1387          $out .= $mac;
1388  
1389          # sodium_memzero(&poly1305_state, sizeof poly1305_state);
1390          unset($auth);
1391  
1392  
1393          # XOR_BUF(STATE_INONCE(state), mac,
1394          #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1395          $st->xorNonce($mac);
1396  
1397          # sodium_increment(STATE_COUNTER(state),
1398          #     crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1399          $st->incrementCounter();
1400          // Overwrite by reference:
1401          $state = $st->toString();
1402  
1403          /** @var bool $rekey */
1404          $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1405          # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1406          #     sodium_is_zero(STATE_COUNTER(state),
1407          #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1408          #     crypto_secretstream_xchacha20poly1305_rekey(state);
1409          # }
1410          if ($rekey || $st->needsRekey()) {
1411              // DO REKEY
1412              self::secretstream_xchacha20poly1305_rekey($state);
1413          }
1414          # if (outlen_p != NULL) {
1415          #     *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
1416          # }
1417          return $out;
1418      }
1419  
1420      /**
1421       * @param string $state
1422       * @param string $cipher
1423       * @param string $aad
1424       * @return bool|array{0: string, 1: int}
1425       * @throws SodiumException
1426       */
1427      public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
1428      {
1429          $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1430  
1431          $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher);
1432          #     mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
1433          $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
1434          $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
1435  
1436          #     if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1437          #         sodium_misuse();
1438          #     }
1439          if ((($msglen + 63) >> 6) > 0xfffffffe) {
1440              throw new SodiumException(
1441                  'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1442              );
1443          }
1444  
1445          #     crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1446          #     crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1447          #     sodium_memzero(block, sizeof block);
1448          $auth = new ParagonIE_Sodium_Core_Poly1305_State(
1449              ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1450          );
1451  
1452          #     crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1453          $auth->update($aad);
1454  
1455          #     crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1456          #         (0x10 - adlen) & 0xf);
1457          $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1458  
1459  
1460          #     memset(block, 0, sizeof block);
1461          #     block[0] = in[0];
1462          #     crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1463          #                                        state->nonce, 1U, state->k);
1464          $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1465              $cipher[0] . str_repeat("\0", 63),
1466              $st->getCombinedNonce(),
1467              $st->getKey(),
1468              ParagonIE_Sodium_Core_Util::store64_le(1)
1469          );
1470          #     tag = block[0];
1471          #     block[0] = in[0];
1472          #     crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1473          $tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]);
1474          $block[0] = $cipher[0];
1475          $auth->update($block);
1476  
1477  
1478          #     c = in + (sizeof tag);
1479          #     crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1480          $auth->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen));
1481  
1482          #     crypto_onetimeauth_poly1305_update
1483          #     (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1484          $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1485  
1486          #     STORE64_LE(slen, (uint64_t) adlen);
1487          #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1488          $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
1489          $auth->update($slen);
1490  
1491          #     STORE64_LE(slen, (sizeof block) + mlen);
1492          #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1493          $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
1494          $auth->update($slen);
1495  
1496          #     crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1497          #     sodium_memzero(&poly1305_state, sizeof poly1305_state);
1498          $mac = $auth->finish();
1499  
1500          #     stored_mac = c + mlen;
1501          #     if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
1502          #     sodium_memzero(mac, sizeof mac);
1503          #         return -1;
1504          #     }
1505  
1506          $stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16);
1507          if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) {
1508              return false;
1509          }
1510  
1511          #     crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
1512          $out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1513              ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen),
1514              $st->getCombinedNonce(),
1515              $st->getKey(),
1516              ParagonIE_Sodium_Core_Util::store64_le(2)
1517          );
1518  
1519          #     XOR_BUF(STATE_INONCE(state), mac,
1520          #         crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1521          $st->xorNonce($mac);
1522  
1523          #     sodium_increment(STATE_COUNTER(state),
1524          #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1525          $st->incrementCounter();
1526  
1527          #     if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1528          #         sodium_is_zero(STATE_COUNTER(state),
1529          #             crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1530          #         crypto_secretstream_xchacha20poly1305_rekey(state);
1531          #     }
1532  
1533          // Overwrite by reference:
1534          $state = $st->toString();
1535  
1536          /** @var bool $rekey */
1537          $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1538          if ($rekey || $st->needsRekey()) {
1539              // DO REKEY
1540              self::secretstream_xchacha20poly1305_rekey($state);
1541          }
1542          return array($out, $tag);
1543      }
1544  
1545      /**
1546       * @param string $state
1547       * @return void
1548       * @throws SodiumException
1549       */
1550      public static function secretstream_xchacha20poly1305_rekey(&$state)
1551      {
1552          $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
1553          # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
1554          # crypto_secretstream_xchacha20poly1305_INONCEBYTES];
1555          # size_t        i;
1556          # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1557          #     new_key_and_inonce[i] = state->k[i];
1558          # }
1559          $new_key_and_inonce = $st->getKey();
1560  
1561          # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1562          #     new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
1563          #         STATE_INONCE(state)[i];
1564          # }
1565          $new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st->getNonce(), 0, 8);
1566  
1567          # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
1568          #                                 sizeof new_key_and_inonce,
1569          #                                 state->nonce, state->k);
1570  
1571          $st->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(
1572              $new_key_and_inonce,
1573              $st->getCombinedNonce(),
1574              $st->getKey(),
1575              ParagonIE_Sodium_Core_Util::store64_le(0)
1576          ));
1577  
1578          # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1579          #     state->k[i] = new_key_and_inonce[i];
1580          # }
1581          # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1582          #     STATE_INONCE(state)[i] =
1583          #          new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
1584          # }
1585          # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1586          $st->counterReset();
1587  
1588          $state = $st->toString();
1589      }
1590  
1591      /**
1592       * Detached Ed25519 signature.
1593       *
1594       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1595       *
1596       * @param string $message
1597       * @param string $sk
1598       * @return string
1599       * @throws SodiumException
1600       * @throws TypeError
1601       */
1602      public static function sign_detached($message, $sk)
1603      {
1604          return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk);
1605      }
1606  
1607      /**
1608       * Attached Ed25519 signature. (Returns a signed message.)
1609       *
1610       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1611       *
1612       * @param string $message
1613       * @param string $sk
1614       * @return string
1615       * @throws SodiumException
1616       * @throws TypeError
1617       */
1618      public static function sign($message, $sk)
1619      {
1620          return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk);
1621      }
1622  
1623      /**
1624       * Opens a signed message. If valid, returns the message.
1625       *
1626       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1627       *
1628       * @param string $signedMessage
1629       * @param string $pk
1630       * @return string
1631       * @throws SodiumException
1632       * @throws TypeError
1633       */
1634      public static function sign_open($signedMessage, $pk)
1635      {
1636          return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk);
1637      }
1638  
1639      /**
1640       * Verify a detached signature of a given message and public key.
1641       *
1642       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1643       *
1644       * @param string $signature
1645       * @param string $message
1646       * @param string $pk
1647       * @return bool
1648       * @throws SodiumException
1649       * @throws TypeError
1650       */
1651      public static function sign_verify_detached($signature, $message, $pk)
1652      {
1653          return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk);
1654      }
1655  }


Generated : Thu Nov 21 08:20:01 2024 Cross-referenced by PHPXref