[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

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

   1  <?php
   2  
   3  if (class_exists('ParagonIE_Sodium_Crypto32', 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_Crypto32
  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_Core32_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_Core32_Util::strlen($ad);
  85  
  86          /** @var string $mac - Message authentication code */
  87          $mac = ParagonIE_Sodium_Core32_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_Core32_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_Core32_ChaCha20::stream(
  98              32,
  99              $nonce,
 100              $key
 101          );
 102  
 103          /* Recalculate the Poly1305 authentication tag (MAC): */
 104          $state = new ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
 112          $state->update($ciphertext);
 113          $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
 114          $computed_mac = $state->finish();
 115  
 116          /* Compare the given MAC with the recalculated MAC: */
 117          if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
 123              $ciphertext,
 124              $nonce,
 125              $key,
 126              ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
 151  
 152          /** @var int $adlen - Length of the associated data */
 153          $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
 154  
 155          /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
 156          $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
 157              32,
 158              $nonce,
 159              $key
 160          );
 161          $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
 170              $message,
 171              $nonce,
 172              $key,
 173              ParagonIE_Sodium_Core32_Util::store64_le(1)
 174          );
 175  
 176          $state->update($ad);
 177          $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
 178          $state->update($ciphertext);
 179          $state->update(ParagonIE_Sodium_Core32_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_Core32_Util::strlen($ad);
 204  
 205          /** @var int $len - Length of message (ciphertext + MAC) */
 206          $len = ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStream(
 213              32,
 214              $nonce,
 215              $key
 216          );
 217  
 218          /** @var string $mac - Message authentication code */
 219          $mac = ParagonIE_Sodium_Core32_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_Core32_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_Core32_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_Core32_Util::store64_le($adlen));
 244          $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
 245          $computed_mac = $state->finish();
 246  
 247          /* Compare the given MAC with the recalculated MAC: */
 248          if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
 254              $ciphertext,
 255              $nonce,
 256              $key,
 257              ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
 282  
 283          /** @var int $adlen - Length of the associated data */
 284          $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
 285  
 286          /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
 287          $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(
 288              32,
 289              $nonce,
 290              $key
 291          );
 292          $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
 301              $message,
 302              $nonce,
 303              $key,
 304              ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
 312          $state->update(ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
 336              ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
 337              $key
 338          );
 339          $nonceLast = "\x00\x00\x00\x00" .
 340              ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
 365              ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
 366              $key
 367          );
 368          $nonceLast = "\x00\x00\x00\x00" .
 369              ParagonIE_Sodium_Core32_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_Core32_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_Core32_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          return self::secretbox(
 428              $plaintext,
 429              $nonce,
 430              self::box_beforenm(
 431                  self::box_secretkey($keypair),
 432                  self::box_publickey($keypair)
 433              )
 434          );
 435      }
 436  
 437      /**
 438       * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
 439       *
 440       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 441       *
 442       * @param string $message
 443       * @param string $publicKey
 444       * @return string
 445       * @throws SodiumException
 446       * @throws TypeError
 447       */
 448      public static function box_seal($message, $publicKey)
 449      {
 450          /** @var string $ephemeralKeypair */
 451          $ephemeralKeypair = self::box_keypair();
 452  
 453          /** @var string $ephemeralSK */
 454          $ephemeralSK = self::box_secretkey($ephemeralKeypair);
 455  
 456          /** @var string $ephemeralPK */
 457          $ephemeralPK = self::box_publickey($ephemeralKeypair);
 458  
 459          /** @var string $nonce */
 460          $nonce = self::generichash(
 461              $ephemeralPK . $publicKey,
 462              '',
 463              24
 464          );
 465  
 466          /** @var string $keypair - The combined keypair used in crypto_box() */
 467          $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);
 468  
 469          /** @var string $ciphertext Ciphertext + MAC from crypto_box */
 470          $ciphertext = self::box($message, $nonce, $keypair);
 471          try {
 472              ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
 473              ParagonIE_Sodium_Compat::memzero($ephemeralSK);
 474              ParagonIE_Sodium_Compat::memzero($nonce);
 475          } catch (SodiumException $ex) {
 476              $ephemeralKeypair = null;
 477              $ephemeralSK = null;
 478              $nonce = null;
 479          }
 480          return $ephemeralPK . $ciphertext;
 481      }
 482  
 483      /**
 484       * Opens a message encrypted via box_seal().
 485       *
 486       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 487       *
 488       * @param string $message
 489       * @param string $keypair
 490       * @return string
 491       * @throws SodiumException
 492       * @throws TypeError
 493       */
 494      public static function box_seal_open($message, $keypair)
 495      {
 496          /** @var string $ephemeralPK */
 497          $ephemeralPK = ParagonIE_Sodium_Core32_Util::substr($message, 0, 32);
 498  
 499          /** @var string $ciphertext (ciphertext + MAC) */
 500          $ciphertext = ParagonIE_Sodium_Core32_Util::substr($message, 32);
 501  
 502          /** @var string $secretKey */
 503          $secretKey = self::box_secretkey($keypair);
 504  
 505          /** @var string $publicKey */
 506          $publicKey = self::box_publickey($keypair);
 507  
 508          /** @var string $nonce */
 509          $nonce = self::generichash(
 510              $ephemeralPK . $publicKey,
 511              '',
 512              24
 513          );
 514  
 515          /** @var string $keypair */
 516          $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);
 517  
 518          /** @var string $m */
 519          $m = self::box_open($ciphertext, $nonce, $keypair);
 520          try {
 521              ParagonIE_Sodium_Compat::memzero($secretKey);
 522              ParagonIE_Sodium_Compat::memzero($ephemeralPK);
 523              ParagonIE_Sodium_Compat::memzero($nonce);
 524          } catch (SodiumException $ex) {
 525              $secretKey = null;
 526              $ephemeralPK = null;
 527              $nonce = null;
 528          }
 529          return $m;
 530      }
 531  
 532      /**
 533       * Used by crypto_box() to get the crypto_secretbox() key.
 534       *
 535       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 536       *
 537       * @param string $sk
 538       * @param string $pk
 539       * @return string
 540       * @throws SodiumException
 541       * @throws TypeError
 542       */
 543      public static function box_beforenm($sk, $pk)
 544      {
 545          return ParagonIE_Sodium_Core32_HSalsa20::hsalsa20(
 546              str_repeat("\x00", 16),
 547              self::scalarmult($sk, $pk)
 548          );
 549      }
 550  
 551      /**
 552       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 553       *
 554       * @return string
 555       * @throws Exception
 556       * @throws SodiumException
 557       * @throws TypeError
 558       */
 559      public static function box_keypair()
 560      {
 561          $sKey = random_bytes(32);
 562          $pKey = self::scalarmult_base($sKey);
 563          return $sKey . $pKey;
 564      }
 565  
 566      /**
 567       * @param string $seed
 568       * @return string
 569       * @throws SodiumException
 570       * @throws TypeError
 571       */
 572      public static function box_seed_keypair($seed)
 573      {
 574          $sKey = ParagonIE_Sodium_Core32_Util::substr(
 575              hash('sha512', $seed, true),
 576              0,
 577              32
 578          );
 579          $pKey = self::scalarmult_base($sKey);
 580          return $sKey . $pKey;
 581      }
 582  
 583      /**
 584       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 585       *
 586       * @param string $sKey
 587       * @param string $pKey
 588       * @return string
 589       * @throws TypeError
 590       */
 591      public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
 592      {
 593          return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) .
 594              ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32);
 595      }
 596  
 597      /**
 598       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 599       *
 600       * @param string $keypair
 601       * @return string
 602       * @throws RangeException
 603       * @throws TypeError
 604       */
 605      public static function box_secretkey($keypair)
 606      {
 607          if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) {
 608              throw new RangeException(
 609                  'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
 610              );
 611          }
 612          return ParagonIE_Sodium_Core32_Util::substr($keypair, 0, 32);
 613      }
 614  
 615      /**
 616       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 617       *
 618       * @param string $keypair
 619       * @return string
 620       * @throws RangeException
 621       * @throws TypeError
 622       */
 623      public static function box_publickey($keypair)
 624      {
 625          if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
 626              throw new RangeException(
 627                  'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.'
 628              );
 629          }
 630          return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32);
 631      }
 632  
 633      /**
 634       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 635       *
 636       * @param string $sKey
 637       * @return string
 638       * @throws RangeException
 639       * @throws SodiumException
 640       * @throws TypeError
 641       */
 642      public static function box_publickey_from_secretkey($sKey)
 643      {
 644          if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
 645              throw new RangeException(
 646                  'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.'
 647              );
 648          }
 649          return self::scalarmult_base($sKey);
 650      }
 651  
 652      /**
 653       * Decrypt a message encrypted with box().
 654       *
 655       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 656       *
 657       * @param string $ciphertext
 658       * @param string $nonce
 659       * @param string $keypair
 660       * @return string
 661       * @throws SodiumException
 662       * @throws TypeError
 663       */
 664      public static function box_open($ciphertext, $nonce, $keypair)
 665      {
 666          return self::secretbox_open(
 667              $ciphertext,
 668              $nonce,
 669              self::box_beforenm(
 670                  self::box_secretkey($keypair),
 671                  self::box_publickey($keypair)
 672              )
 673          );
 674      }
 675  
 676      /**
 677       * Calculate a BLAKE2b hash.
 678       *
 679       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 680       *
 681       * @param string $message
 682       * @param string|null $key
 683       * @param int $outlen
 684       * @return string
 685       * @throws RangeException
 686       * @throws SodiumException
 687       * @throws TypeError
 688       */
 689      public static function generichash($message, $key = '', $outlen = 32)
 690      {
 691          // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
 692          ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
 693  
 694          $k = null;
 695          if (!empty($key)) {
 696              /** @var SplFixedArray $k */
 697              $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
 698              if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
 699                  throw new RangeException('Invalid key size');
 700              }
 701          }
 702  
 703          /** @var SplFixedArray $in */
 704          $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
 705  
 706          /** @var SplFixedArray $ctx */
 707          $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen);
 708          ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count());
 709  
 710          /** @var SplFixedArray $out */
 711          $out = new SplFixedArray($outlen);
 712          $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out);
 713  
 714          /** @var array<int, int> */
 715          $outArray = $out->toArray();
 716          return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
 717      }
 718  
 719      /**
 720       * Finalize a BLAKE2b hashing context, returning the hash.
 721       *
 722       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 723       *
 724       * @param string $ctx
 725       * @param int $outlen
 726       * @return string
 727       * @throws SodiumException
 728       * @throws TypeError
 729       */
 730      public static function generichash_final($ctx, $outlen = 32)
 731      {
 732          if (!is_string($ctx)) {
 733              throw new TypeError('Context must be a string');
 734          }
 735          $out = new SplFixedArray($outlen);
 736  
 737          /** @var SplFixedArray $context */
 738          $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
 739  
 740          /** @var SplFixedArray $out */
 741          $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out);
 742  
 743          /** @var array<int, int> */
 744          $outArray = $out->toArray();
 745          return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
 746      }
 747  
 748      /**
 749       * Initialize a hashing context for BLAKE2b.
 750       *
 751       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 752       *
 753       * @param string $key
 754       * @param int $outputLength
 755       * @return string
 756       * @throws RangeException
 757       * @throws SodiumException
 758       * @throws TypeError
 759       */
 760      public static function generichash_init($key = '', $outputLength = 32)
 761      {
 762          // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
 763          ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
 764  
 765          $k = null;
 766          if (!empty($key)) {
 767              $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
 768              if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
 769                  throw new RangeException('Invalid key size');
 770              }
 771          }
 772  
 773          /** @var SplFixedArray $ctx */
 774          $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength);
 775  
 776          return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
 777      }
 778  
 779      /**
 780       * Initialize a hashing context for BLAKE2b.
 781       *
 782       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 783       *
 784       * @param string $key
 785       * @param int $outputLength
 786       * @param string $salt
 787       * @param string $personal
 788       * @return string
 789       * @throws RangeException
 790       * @throws SodiumException
 791       * @throws TypeError
 792       */
 793      public static function generichash_init_salt_personal(
 794          $key = '',
 795          $outputLength = 32,
 796          $salt = '',
 797          $personal = ''
 798      ) {
 799          // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
 800          ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
 801  
 802          $k = null;
 803          if (!empty($key)) {
 804              $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
 805              if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
 806                  throw new RangeException('Invalid key size');
 807              }
 808          }
 809          if (!empty($salt)) {
 810              $s = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($salt);
 811          } else {
 812              $s = null;
 813          }
 814          if (!empty($salt)) {
 815              $p = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($personal);
 816          } else {
 817              $p = null;
 818          }
 819  
 820          /** @var SplFixedArray $ctx */
 821          $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength, $s, $p);
 822  
 823          return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
 824      }
 825  
 826      /**
 827       * Update a hashing context for BLAKE2b with $message
 828       *
 829       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 830       *
 831       * @param string $ctx
 832       * @param string $message
 833       * @return string
 834       * @throws SodiumException
 835       * @throws TypeError
 836       */
 837      public static function generichash_update($ctx, $message)
 838      {
 839          // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
 840          ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
 841  
 842          /** @var SplFixedArray $context */
 843          $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
 844  
 845          /** @var SplFixedArray $in */
 846          $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
 847  
 848          ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
 849  
 850          return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
 851      }
 852  
 853      /**
 854       * Libsodium's crypto_kx().
 855       *
 856       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 857       *
 858       * @param string $my_sk
 859       * @param string $their_pk
 860       * @param string $client_pk
 861       * @param string $server_pk
 862       * @return string
 863       * @throws SodiumException
 864       * @throws TypeError
 865       */
 866      public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
 867      {
 868          return self::generichash(
 869              self::scalarmult($my_sk, $their_pk) .
 870              $client_pk .
 871              $server_pk
 872          );
 873      }
 874  
 875      /**
 876       * ECDH over Curve25519
 877       *
 878       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 879       *
 880       * @param string $sKey
 881       * @param string $pKey
 882       * @return string
 883       *
 884       * @throws SodiumException
 885       * @throws TypeError
 886       */
 887      public static function scalarmult($sKey, $pKey)
 888      {
 889          $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
 890          self::scalarmult_throw_if_zero($q);
 891          return $q;
 892      }
 893  
 894      /**
 895       * ECDH over Curve25519, using the basepoint.
 896       * Used to get a secret key from a public key.
 897       *
 898       * @param string $secret
 899       * @return string
 900       *
 901       * @throws SodiumException
 902       * @throws TypeError
 903       */
 904      public static function scalarmult_base($secret)
 905      {
 906          $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
 907          self::scalarmult_throw_if_zero($q);
 908          return $q;
 909      }
 910  
 911      /**
 912       * This throws an Error if a zero public key was passed to the function.
 913       *
 914       * @param string $q
 915       * @return void
 916       * @throws SodiumException
 917       * @throws TypeError
 918       */
 919      protected static function scalarmult_throw_if_zero($q)
 920      {
 921          $d = 0;
 922          for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
 923              $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
 924          }
 925  
 926          /* branch-free variant of === 0 */
 927          if (-(1 & (($d - 1) >> 8))) {
 928              throw new SodiumException('Zero public key is not allowed');
 929          }
 930      }
 931  
 932      /**
 933       * XSalsa20-Poly1305 authenticated symmetric-key encryption.
 934       *
 935       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 936       *
 937       * @param string $plaintext
 938       * @param string $nonce
 939       * @param string $key
 940       * @return string
 941       * @throws SodiumException
 942       * @throws TypeError
 943       */
 944      public static function secretbox($plaintext, $nonce, $key)
 945      {
 946          /** @var string $subkey */
 947          $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
 948  
 949          /** @var string $block0 */
 950          $block0 = str_repeat("\x00", 32);
 951  
 952          /** @var int $mlen - Length of the plaintext message */
 953          $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
 954          $mlen0 = $mlen;
 955          if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
 956              $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
 957          }
 958          $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
 959  
 960          /** @var string $block0 */
 961          $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
 962              $block0,
 963              ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
 964              $subkey
 965          );
 966  
 967          /** @var string $c */
 968          $c = ParagonIE_Sodium_Core32_Util::substr(
 969              $block0,
 970              self::secretbox_xsalsa20poly1305_ZEROBYTES
 971          );
 972          if ($mlen > $mlen0) {
 973              $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
 974                  ParagonIE_Sodium_Core32_Util::substr(
 975                      $plaintext,
 976                      self::secretbox_xsalsa20poly1305_ZEROBYTES
 977                  ),
 978                  ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
 979                  1,
 980                  $subkey
 981              );
 982          }
 983          $state = new ParagonIE_Sodium_Core32_Poly1305_State(
 984              ParagonIE_Sodium_Core32_Util::substr(
 985                  $block0,
 986                  0,
 987                  self::onetimeauth_poly1305_KEYBYTES
 988              )
 989          );
 990          try {
 991              ParagonIE_Sodium_Compat::memzero($block0);
 992              ParagonIE_Sodium_Compat::memzero($subkey);
 993          } catch (SodiumException $ex) {
 994              $block0 = null;
 995              $subkey = null;
 996          }
 997  
 998          $state->update($c);
 999  
1000          /** @var string $c - MAC || ciphertext */
1001          $c = $state->finish() . $c;
1002          unset($state);
1003  
1004          return $c;
1005      }
1006  
1007      /**
1008       * Decrypt a ciphertext generated via secretbox().
1009       *
1010       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1011       *
1012       * @param string $ciphertext
1013       * @param string $nonce
1014       * @param string $key
1015       * @return string
1016       * @throws SodiumException
1017       * @throws TypeError
1018       */
1019      public static function secretbox_open($ciphertext, $nonce, $key)
1020      {
1021          /** @var string $mac */
1022          $mac = ParagonIE_Sodium_Core32_Util::substr(
1023              $ciphertext,
1024              0,
1025              self::secretbox_xsalsa20poly1305_MACBYTES
1026          );
1027  
1028          /** @var string $c */
1029          $c = ParagonIE_Sodium_Core32_Util::substr(
1030              $ciphertext,
1031              self::secretbox_xsalsa20poly1305_MACBYTES
1032          );
1033  
1034          /** @var int $clen */
1035          $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
1036  
1037          /** @var string $subkey */
1038          $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
1039  
1040          /** @var string $block0 */
1041          $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
1042              64,
1043              ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1044              $subkey
1045          );
1046          $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
1047              $mac,
1048              $c,
1049              ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
1050          );
1051          if (!$verified) {
1052              try {
1053                  ParagonIE_Sodium_Compat::memzero($subkey);
1054              } catch (SodiumException $ex) {
1055                  $subkey = null;
1056              }
1057              throw new SodiumException('Invalid MAC');
1058          }
1059  
1060          /** @var string $m - Decrypted message */
1061          $m = ParagonIE_Sodium_Core32_Util::xorStrings(
1062              ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
1063              ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
1064          );
1065          if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
1066              // We had more than 1 block, so let's continue to decrypt the rest.
1067              $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
1068                  ParagonIE_Sodium_Core32_Util::substr(
1069                      $c,
1070                      self::secretbox_xsalsa20poly1305_ZEROBYTES
1071                  ),
1072                  ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1073                  1,
1074                  (string) $subkey
1075              );
1076          }
1077          return $m;
1078      }
1079  
1080      /**
1081       * XChaCha20-Poly1305 authenticated symmetric-key encryption.
1082       *
1083       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1084       *
1085       * @param string $plaintext
1086       * @param string $nonce
1087       * @param string $key
1088       * @return string
1089       * @throws SodiumException
1090       * @throws TypeError
1091       */
1092      public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
1093      {
1094          /** @var string $subkey */
1095          $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
1096              ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
1097              $key
1098          );
1099          $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
1100  
1101          /** @var string $block0 */
1102          $block0 = str_repeat("\x00", 32);
1103  
1104          /** @var int $mlen - Length of the plaintext message */
1105          $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
1106          $mlen0 = $mlen;
1107          if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
1108              $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
1109          }
1110          $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
1111  
1112          /** @var string $block0 */
1113          $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1114              $block0,
1115              $nonceLast,
1116              $subkey
1117          );
1118  
1119          /** @var string $c */
1120          $c = ParagonIE_Sodium_Core32_Util::substr(
1121              $block0,
1122              self::secretbox_xchacha20poly1305_ZEROBYTES
1123          );
1124          if ($mlen > $mlen0) {
1125              $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1126                  ParagonIE_Sodium_Core32_Util::substr(
1127                      $plaintext,
1128                      self::secretbox_xchacha20poly1305_ZEROBYTES
1129                  ),
1130                  $nonceLast,
1131                  $subkey,
1132                  ParagonIE_Sodium_Core32_Util::store64_le(1)
1133              );
1134          }
1135          $state = new ParagonIE_Sodium_Core32_Poly1305_State(
1136              ParagonIE_Sodium_Core32_Util::substr(
1137                  $block0,
1138                  0,
1139                  self::onetimeauth_poly1305_KEYBYTES
1140              )
1141          );
1142          try {
1143              ParagonIE_Sodium_Compat::memzero($block0);
1144              ParagonIE_Sodium_Compat::memzero($subkey);
1145          } catch (SodiumException $ex) {
1146              $block0 = null;
1147              $subkey = null;
1148          }
1149  
1150          $state->update($c);
1151  
1152          /** @var string $c - MAC || ciphertext */
1153          $c = $state->finish() . $c;
1154          unset($state);
1155  
1156          return $c;
1157      }
1158  
1159      /**
1160       * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
1161       *
1162       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1163       *
1164       * @param string $ciphertext
1165       * @param string $nonce
1166       * @param string $key
1167       * @return string
1168       * @throws SodiumException
1169       * @throws TypeError
1170       */
1171      public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
1172      {
1173          /** @var string $mac */
1174          $mac = ParagonIE_Sodium_Core32_Util::substr(
1175              $ciphertext,
1176              0,
1177              self::secretbox_xchacha20poly1305_MACBYTES
1178          );
1179  
1180          /** @var string $c */
1181          $c = ParagonIE_Sodium_Core32_Util::substr(
1182              $ciphertext,
1183              self::secretbox_xchacha20poly1305_MACBYTES
1184          );
1185  
1186          /** @var int $clen */
1187          $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
1188  
1189          /** @var string $subkey */
1190          $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
1191  
1192          /** @var string $block0 */
1193          $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
1194              64,
1195              ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1196              $subkey
1197          );
1198          $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
1199              $mac,
1200              $c,
1201              ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
1202          );
1203  
1204          if (!$verified) {
1205              try {
1206                  ParagonIE_Sodium_Compat::memzero($subkey);
1207              } catch (SodiumException $ex) {
1208                  $subkey = null;
1209              }
1210              throw new SodiumException('Invalid MAC');
1211          }
1212  
1213          /** @var string $m - Decrypted message */
1214          $m = ParagonIE_Sodium_Core32_Util::xorStrings(
1215              ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
1216              ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
1217          );
1218  
1219          if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
1220              // We had more than 1 block, so let's continue to decrypt the rest.
1221              $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1222                  ParagonIE_Sodium_Core32_Util::substr(
1223                      $c,
1224                      self::secretbox_xchacha20poly1305_ZEROBYTES
1225                  ),
1226                  ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1227                  (string) $subkey,
1228                  ParagonIE_Sodium_Core32_Util::store64_le(1)
1229              );
1230          }
1231          return $m;
1232      }
1233  
1234      /**
1235       * @param string $key
1236       * @return array<int, string> Returns a state and a header.
1237       * @throws Exception
1238       * @throws SodiumException
1239       */
1240      public static function secretstream_xchacha20poly1305_init_push($key)
1241      {
1242          # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
1243          $out = random_bytes(24);
1244  
1245          # crypto_core_hchacha20(state->k, out, k, NULL);
1246          $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20($out, $key);
1247          $state = new ParagonIE_Sodium_Core32_SecretStream_State(
1248              $subkey,
1249              ParagonIE_Sodium_Core32_Util::substr($out, 16, 8) . str_repeat("\0", 4)
1250          );
1251  
1252          # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1253          $state->counterReset();
1254  
1255          # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,
1256          #        crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1257          # memset(state->_pad, 0, sizeof state->_pad);
1258          return array(
1259              $state->toString(),
1260              $out
1261          );
1262      }
1263  
1264      /**
1265       * @param string $key
1266       * @param string $header
1267       * @return string Returns a state.
1268       * @throws Exception
1269       */
1270      public static function secretstream_xchacha20poly1305_init_pull($key, $header)
1271      {
1272          # crypto_core_hchacha20(state->k, in, k, NULL);
1273          $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
1274              ParagonIE_Sodium_Core32_Util::substr($header, 0, 16),
1275              $key
1276          );
1277          $state = new ParagonIE_Sodium_Core32_SecretStream_State(
1278              $subkey,
1279              ParagonIE_Sodium_Core32_Util::substr($header, 16)
1280          );
1281          $state->counterReset();
1282          # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,
1283          #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1284          # memset(state->_pad, 0, sizeof state->_pad);
1285          # return 0;
1286          return $state->toString();
1287      }
1288  
1289      /**
1290       * @param string $state
1291       * @param string $msg
1292       * @param string $aad
1293       * @param int $tag
1294       * @return string
1295       * @throws SodiumException
1296       */
1297      public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0)
1298      {
1299          $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
1300          # crypto_onetimeauth_poly1305_state poly1305_state;
1301          # unsigned char                     block[64U];
1302          # unsigned char                     slen[8U];
1303          # unsigned char                    *c;
1304          # unsigned char                    *mac;
1305  
1306          $msglen = ParagonIE_Sodium_Core32_Util::strlen($msg);
1307          $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
1308  
1309          if ((($msglen + 63) >> 6) > 0xfffffffe) {
1310              throw new SodiumException(
1311                  'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1312              );
1313          }
1314  
1315          # if (outlen_p != NULL) {
1316          #     *outlen_p = 0U;
1317          # }
1318          # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1319          #     sodium_misuse();
1320          # }
1321  
1322          # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1323          # crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1324          # sodium_memzero(block, sizeof block);
1325          $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
1326              ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1327          );
1328  
1329          # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1330          $auth->update($aad);
1331  
1332          # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1333          #     (0x10 - adlen) & 0xf);
1334          $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1335  
1336          # memset(block, 0, sizeof block);
1337          # block[0] = tag;
1338          # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1339          #                                    state->nonce, 1U, state->k);
1340          $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1341              ParagonIE_Sodium_Core32_Util::intToChr($tag) . str_repeat("\0", 63),
1342              $st->getCombinedNonce(),
1343              $st->getKey(),
1344              ParagonIE_Sodium_Core32_Util::store64_le(1)
1345          );
1346  
1347          # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1348          $auth->update($block);
1349  
1350          # out[0] = block[0];
1351          $out = $block[0];
1352          # c = out + (sizeof tag);
1353          # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
1354          $cipher = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1355              $msg,
1356              $st->getCombinedNonce(),
1357              $st->getKey(),
1358              ParagonIE_Sodium_Core32_Util::store64_le(2)
1359          );
1360  
1361          # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1362          $auth->update($cipher);
1363  
1364          $out .= $cipher;
1365          unset($cipher);
1366  
1367          # crypto_onetimeauth_poly1305_update
1368          # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1369          $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1370  
1371          # STORE64_LE(slen, (uint64_t) adlen);
1372          $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
1373  
1374          # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1375          $auth->update($slen);
1376  
1377          # STORE64_LE(slen, (sizeof block) + mlen);
1378          $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
1379  
1380          # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1381          $auth->update($slen);
1382  
1383          # mac = c + mlen;
1384          # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1385          $mac = $auth->finish();
1386          $out .= $mac;
1387  
1388          # sodium_memzero(&poly1305_state, sizeof poly1305_state);
1389          unset($auth);
1390  
1391  
1392          # XOR_BUF(STATE_INONCE(state), mac,
1393          #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1394          $st->xorNonce($mac);
1395  
1396          # sodium_increment(STATE_COUNTER(state),
1397          #     crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1398          $st->incrementCounter();
1399          // Overwrite by reference:
1400          $state = $st->toString();
1401  
1402          /** @var bool $rekey */
1403          $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1404          # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1405          #     sodium_is_zero(STATE_COUNTER(state),
1406          #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1407          #     crypto_secretstream_xchacha20poly1305_rekey(state);
1408          # }
1409          if ($rekey || $st->needsRekey()) {
1410              // DO REKEY
1411              self::secretstream_xchacha20poly1305_rekey($state);
1412          }
1413          # if (outlen_p != NULL) {
1414          #     *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;
1415          # }
1416          return $out;
1417      }
1418  
1419      /**
1420       * @param string $state
1421       * @param string $cipher
1422       * @param string $aad
1423       * @return bool|array{0: string, 1: int}
1424       * @throws SodiumException
1425       */
1426      public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '')
1427      {
1428          $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
1429  
1430          $cipherlen = ParagonIE_Sodium_Core32_Util::strlen($cipher);
1431          #     mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
1432          $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
1433          $aadlen = ParagonIE_Sodium_Core32_Util::strlen($aad);
1434  
1435          #     if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {
1436          #         sodium_misuse();
1437          #     }
1438          if ((($msglen + 63) >> 6) > 0xfffffffe) {
1439              throw new SodiumException(
1440                  'message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes'
1441              );
1442          }
1443  
1444          #     crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);
1445          #     crypto_onetimeauth_poly1305_init(&poly1305_state, block);
1446          #     sodium_memzero(block, sizeof block);
1447          $auth = new ParagonIE_Sodium_Core32_Poly1305_State(
1448              ParagonIE_Sodium_Core32_ChaCha20::ietfStream(32, $st->getCombinedNonce(), $st->getKey())
1449          );
1450  
1451          #     crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
1452          $auth->update($aad);
1453  
1454          #     crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,
1455          #         (0x10 - adlen) & 0xf);
1456          $auth->update(str_repeat("\0", ((0x10 - $aadlen) & 0xf)));
1457  
1458  
1459          #     memset(block, 0, sizeof block);
1460          #     block[0] = in[0];
1461          #     crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,
1462          #                                        state->nonce, 1U, state->k);
1463          $block = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1464              $cipher[0] . str_repeat("\0", 63),
1465              $st->getCombinedNonce(),
1466              $st->getKey(),
1467              ParagonIE_Sodium_Core32_Util::store64_le(1)
1468          );
1469          #     tag = block[0];
1470          #     block[0] = in[0];
1471          #     crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
1472          $tag = ParagonIE_Sodium_Core32_Util::chrToInt($block[0]);
1473          $block[0] = $cipher[0];
1474          $auth->update($block);
1475  
1476  
1477          #     c = in + (sizeof tag);
1478          #     crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
1479          $auth->update(ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen));
1480  
1481          #     crypto_onetimeauth_poly1305_update
1482          #     (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
1483          $auth->update(str_repeat("\0", ((0x10 - 64 + $msglen) & 0xf)));
1484  
1485          #     STORE64_LE(slen, (uint64_t) adlen);
1486          #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1487          $slen = ParagonIE_Sodium_Core32_Util::store64_le($aadlen);
1488          $auth->update($slen);
1489  
1490          #     STORE64_LE(slen, (sizeof block) + mlen);
1491          #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
1492          $slen = ParagonIE_Sodium_Core32_Util::store64_le(64 + $msglen);
1493          $auth->update($slen);
1494  
1495          #     crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
1496          #     sodium_memzero(&poly1305_state, sizeof poly1305_state);
1497          $mac = $auth->finish();
1498  
1499          #     stored_mac = c + mlen;
1500          #     if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {
1501          #     sodium_memzero(mac, sizeof mac);
1502          #         return -1;
1503          #     }
1504  
1505          $stored = ParagonIE_Sodium_Core32_Util::substr($cipher, $msglen + 1, 16);
1506          if (!ParagonIE_Sodium_Core32_Util::hashEquals($mac, $stored)) {
1507              return false;
1508          }
1509  
1510          #     crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
1511          $out = ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1512              ParagonIE_Sodium_Core32_Util::substr($cipher, 1, $msglen),
1513              $st->getCombinedNonce(),
1514              $st->getKey(),
1515              ParagonIE_Sodium_Core32_Util::store64_le(2)
1516          );
1517  
1518          #     XOR_BUF(STATE_INONCE(state), mac,
1519          #         crypto_secretstream_xchacha20poly1305_INONCEBYTES);
1520          $st->xorNonce($mac);
1521  
1522          #     sodium_increment(STATE_COUNTER(state),
1523          #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
1524          $st->incrementCounter();
1525  
1526          #     if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||
1527          #         sodium_is_zero(STATE_COUNTER(state),
1528          #             crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {
1529          #         crypto_secretstream_xchacha20poly1305_rekey(state);
1530          #     }
1531  
1532          // Overwrite by reference:
1533          $state = $st->toString();
1534  
1535          /** @var bool $rekey */
1536          $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
1537          if ($rekey || $st->needsRekey()) {
1538              // DO REKEY
1539              self::secretstream_xchacha20poly1305_rekey($state);
1540          }
1541          return array($out, $tag);
1542      }
1543  
1544      /**
1545       * @param string $state
1546       * @return void
1547       * @throws SodiumException
1548       */
1549      public static function secretstream_xchacha20poly1305_rekey(&$state)
1550      {
1551          $st = ParagonIE_Sodium_Core32_SecretStream_State::fromString($state);
1552          # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +
1553          # crypto_secretstream_xchacha20poly1305_INONCEBYTES];
1554          # size_t        i;
1555          # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1556          #     new_key_and_inonce[i] = state->k[i];
1557          # }
1558          $new_key_and_inonce = $st->getKey();
1559  
1560          # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1561          #     new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =
1562          #         STATE_INONCE(state)[i];
1563          # }
1564          $new_key_and_inonce .= ParagonIE_Sodium_Core32_Util::substR($st->getNonce(), 0, 8);
1565  
1566          # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,
1567          #                                 sizeof new_key_and_inonce,
1568          #                                 state->nonce, state->k);
1569  
1570          $st->rekey(ParagonIE_Sodium_Core32_ChaCha20::ietfStreamXorIc(
1571              $new_key_and_inonce,
1572              $st->getCombinedNonce(),
1573              $st->getKey(),
1574              ParagonIE_Sodium_Core32_Util::store64_le(0)
1575          ));
1576  
1577          # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {
1578          #     state->k[i] = new_key_and_inonce[i];
1579          # }
1580          # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {
1581          #     STATE_INONCE(state)[i] =
1582          #          new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];
1583          # }
1584          # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
1585          $st->counterReset();
1586  
1587          $state = $st->toString();
1588      }
1589  
1590      /**
1591       * Detached Ed25519 signature.
1592       *
1593       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1594       *
1595       * @param string $message
1596       * @param string $sk
1597       * @return string
1598       * @throws SodiumException
1599       * @throws TypeError
1600       */
1601      public static function sign_detached($message, $sk)
1602      {
1603          return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk);
1604      }
1605  
1606      /**
1607       * Attached Ed25519 signature. (Returns a signed message.)
1608       *
1609       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1610       *
1611       * @param string $message
1612       * @param string $sk
1613       * @return string
1614       * @throws SodiumException
1615       * @throws TypeError
1616       */
1617      public static function sign($message, $sk)
1618      {
1619          return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk);
1620      }
1621  
1622      /**
1623       * Opens a signed message. If valid, returns the message.
1624       *
1625       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1626       *
1627       * @param string $signedMessage
1628       * @param string $pk
1629       * @return string
1630       * @throws SodiumException
1631       * @throws TypeError
1632       */
1633      public static function sign_open($signedMessage, $pk)
1634      {
1635          return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk);
1636      }
1637  
1638      /**
1639       * Verify a detached signature of a given message and public key.
1640       *
1641       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1642       *
1643       * @param string $signature
1644       * @param string $message
1645       * @param string $pk
1646       * @return bool
1647       * @throws SodiumException
1648       * @throws TypeError
1649       */
1650      public static function sign_verify_detached($signature, $message, $pk)
1651      {
1652          return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk);
1653      }
1654  }


Generated: Tue Mar 19 01:00:02 2024 Cross-referenced by PHPXref 0.7.1