[ Index ]

PHP Cross Reference of WordPress

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       * Update a hashing context for BLAKE2b with $message
 782       *
 783       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 784       *
 785       * @param string $ctx
 786       * @param string $message
 787       * @return string
 788       * @throws SodiumException
 789       * @throws TypeError
 790       */
 791      public static function generichash_update($ctx, $message)
 792      {
 793          // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
 794          ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
 795  
 796          /** @var SplFixedArray $context */
 797          $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);
 798  
 799          /** @var SplFixedArray $in */
 800          $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
 801  
 802          ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count());
 803  
 804          return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context);
 805      }
 806  
 807      /**
 808       * Libsodium's crypto_kx().
 809       *
 810       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 811       *
 812       * @param string $my_sk
 813       * @param string $their_pk
 814       * @param string $client_pk
 815       * @param string $server_pk
 816       * @return string
 817       * @throws SodiumException
 818       * @throws TypeError
 819       */
 820      public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
 821      {
 822          return ParagonIE_Sodium_Compat::crypto_generichash(
 823              ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) .
 824              $client_pk .
 825              $server_pk
 826          );
 827      }
 828  
 829      /**
 830       * ECDH over Curve25519
 831       *
 832       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 833       *
 834       * @param string $sKey
 835       * @param string $pKey
 836       * @return string
 837       *
 838       * @throws SodiumException
 839       * @throws TypeError
 840       */
 841      public static function scalarmult($sKey, $pKey)
 842      {
 843          $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
 844          self::scalarmult_throw_if_zero($q);
 845          return $q;
 846      }
 847  
 848      /**
 849       * ECDH over Curve25519, using the basepoint.
 850       * Used to get a secret key from a public key.
 851       *
 852       * @param string $secret
 853       * @return string
 854       *
 855       * @throws SodiumException
 856       * @throws TypeError
 857       */
 858      public static function scalarmult_base($secret)
 859      {
 860          $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
 861          self::scalarmult_throw_if_zero($q);
 862          return $q;
 863      }
 864  
 865      /**
 866       * This throws an Error if a zero public key was passed to the function.
 867       *
 868       * @param string $q
 869       * @return void
 870       * @throws SodiumException
 871       * @throws TypeError
 872       */
 873      protected static function scalarmult_throw_if_zero($q)
 874      {
 875          $d = 0;
 876          for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
 877              $d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]);
 878          }
 879  
 880          /* branch-free variant of === 0 */
 881          if (-(1 & (($d - 1) >> 8))) {
 882              throw new SodiumException('Zero public key is not allowed');
 883          }
 884      }
 885  
 886      /**
 887       * XSalsa20-Poly1305 authenticated symmetric-key encryption.
 888       *
 889       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 890       *
 891       * @param string $plaintext
 892       * @param string $nonce
 893       * @param string $key
 894       * @return string
 895       * @throws SodiumException
 896       * @throws TypeError
 897       */
 898      public static function secretbox($plaintext, $nonce, $key)
 899      {
 900          /** @var string $subkey */
 901          $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
 902  
 903          /** @var string $block0 */
 904          $block0 = str_repeat("\x00", 32);
 905  
 906          /** @var int $mlen - Length of the plaintext message */
 907          $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
 908          $mlen0 = $mlen;
 909          if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
 910              $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
 911          }
 912          $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
 913  
 914          /** @var string $block0 */
 915          $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor(
 916              $block0,
 917              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
 918              $subkey
 919          );
 920  
 921          /** @var string $c */
 922          $c = ParagonIE_Sodium_Core_Util::substr(
 923              $block0,
 924              self::secretbox_xsalsa20poly1305_ZEROBYTES
 925          );
 926          if ($mlen > $mlen0) {
 927              $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
 928                  ParagonIE_Sodium_Core_Util::substr(
 929                      $plaintext,
 930                      self::secretbox_xsalsa20poly1305_ZEROBYTES
 931                  ),
 932                  ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
 933                  1,
 934                  $subkey
 935              );
 936          }
 937          $state = new ParagonIE_Sodium_Core_Poly1305_State(
 938              ParagonIE_Sodium_Core_Util::substr(
 939                  $block0,
 940                  0,
 941                  self::onetimeauth_poly1305_KEYBYTES
 942              )
 943          );
 944          try {
 945              ParagonIE_Sodium_Compat::memzero($block0);
 946              ParagonIE_Sodium_Compat::memzero($subkey);
 947          } catch (SodiumException $ex) {
 948              $block0 = null;
 949              $subkey = null;
 950          }
 951  
 952          $state->update($c);
 953  
 954          /** @var string $c - MAC || ciphertext */
 955          $c = $state->finish() . $c;
 956          unset($state);
 957  
 958          return $c;
 959      }
 960  
 961      /**
 962       * Decrypt a ciphertext generated via secretbox().
 963       *
 964       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
 965       *
 966       * @param string $ciphertext
 967       * @param string $nonce
 968       * @param string $key
 969       * @return string
 970       * @throws SodiumException
 971       * @throws TypeError
 972       */
 973      public static function secretbox_open($ciphertext, $nonce, $key)
 974      {
 975          /** @var string $mac */
 976          $mac = ParagonIE_Sodium_Core_Util::substr(
 977              $ciphertext,
 978              0,
 979              self::secretbox_xsalsa20poly1305_MACBYTES
 980          );
 981  
 982          /** @var string $c */
 983          $c = ParagonIE_Sodium_Core_Util::substr(
 984              $ciphertext,
 985              self::secretbox_xsalsa20poly1305_MACBYTES
 986          );
 987  
 988          /** @var int $clen */
 989          $clen = ParagonIE_Sodium_Core_Util::strlen($c);
 990  
 991          /** @var string $subkey */
 992          $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);
 993  
 994          /** @var string $block0 */
 995          $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(
 996              64,
 997              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
 998              $subkey
 999          );
1000          $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
1001              $mac,
1002              $c,
1003              ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
1004          );
1005          if (!$verified) {
1006              try {
1007                  ParagonIE_Sodium_Compat::memzero($subkey);
1008              } catch (SodiumException $ex) {
1009                  $subkey = null;
1010              }
1011              throw new SodiumException('Invalid MAC');
1012          }
1013  
1014          /** @var string $m - Decrypted message */
1015          $m = ParagonIE_Sodium_Core_Util::xorStrings(
1016              ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
1017              ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
1018          );
1019          if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
1020              // We had more than 1 block, so let's continue to decrypt the rest.
1021              $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(
1022                  ParagonIE_Sodium_Core_Util::substr(
1023                      $c,
1024                      self::secretbox_xsalsa20poly1305_ZEROBYTES
1025                  ),
1026                  ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1027                  1,
1028                  (string) $subkey
1029              );
1030          }
1031          return $m;
1032      }
1033  
1034      /**
1035       * XChaCha20-Poly1305 authenticated symmetric-key encryption.
1036       *
1037       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1038       *
1039       * @param string $plaintext
1040       * @param string $nonce
1041       * @param string $key
1042       * @return string
1043       * @throws SodiumException
1044       * @throws TypeError
1045       */
1046      public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
1047      {
1048          /** @var string $subkey */
1049          $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(
1050              ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16),
1051              $key
1052          );
1053          $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
1054  
1055          /** @var string $block0 */
1056          $block0 = str_repeat("\x00", 32);
1057  
1058          /** @var int $mlen - Length of the plaintext message */
1059          $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
1060          $mlen0 = $mlen;
1061          if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
1062              $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
1063          }
1064          $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);
1065  
1066          /** @var string $block0 */
1067          $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1068              $block0,
1069              $nonceLast,
1070              $subkey
1071          );
1072  
1073          /** @var string $c */
1074          $c = ParagonIE_Sodium_Core_Util::substr(
1075              $block0,
1076              self::secretbox_xchacha20poly1305_ZEROBYTES
1077          );
1078          if ($mlen > $mlen0) {
1079              $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1080                  ParagonIE_Sodium_Core_Util::substr(
1081                      $plaintext,
1082                      self::secretbox_xchacha20poly1305_ZEROBYTES
1083                  ),
1084                  $nonceLast,
1085                  $subkey,
1086                  ParagonIE_Sodium_Core_Util::store64_le(1)
1087              );
1088          }
1089          $state = new ParagonIE_Sodium_Core_Poly1305_State(
1090              ParagonIE_Sodium_Core_Util::substr(
1091                  $block0,
1092                  0,
1093                  self::onetimeauth_poly1305_KEYBYTES
1094              )
1095          );
1096          try {
1097              ParagonIE_Sodium_Compat::memzero($block0);
1098              ParagonIE_Sodium_Compat::memzero($subkey);
1099          } catch (SodiumException $ex) {
1100              $block0 = null;
1101              $subkey = null;
1102          }
1103  
1104          $state->update($c);
1105  
1106          /** @var string $c - MAC || ciphertext */
1107          $c = $state->finish() . $c;
1108          unset($state);
1109  
1110          return $c;
1111      }
1112  
1113      /**
1114       * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
1115       *
1116       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1117       *
1118       * @param string $ciphertext
1119       * @param string $nonce
1120       * @param string $key
1121       * @return string
1122       * @throws SodiumException
1123       * @throws TypeError
1124       */
1125      public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
1126      {
1127          /** @var string $mac */
1128          $mac = ParagonIE_Sodium_Core_Util::substr(
1129              $ciphertext,
1130              0,
1131              self::secretbox_xchacha20poly1305_MACBYTES
1132          );
1133  
1134          /** @var string $c */
1135          $c = ParagonIE_Sodium_Core_Util::substr(
1136              $ciphertext,
1137              self::secretbox_xchacha20poly1305_MACBYTES
1138          );
1139  
1140          /** @var int $clen */
1141          $clen = ParagonIE_Sodium_Core_Util::strlen($c);
1142  
1143          /** @var string $subkey */
1144          $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key);
1145  
1146          /** @var string $block0 */
1147          $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(
1148              64,
1149              ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1150              $subkey
1151          );
1152          $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify(
1153              $mac,
1154              $c,
1155              ParagonIE_Sodium_Core_Util::substr($block0, 0, 32)
1156          );
1157  
1158          if (!$verified) {
1159              try {
1160                  ParagonIE_Sodium_Compat::memzero($subkey);
1161              } catch (SodiumException $ex) {
1162                  $subkey = null;
1163              }
1164              throw new SodiumException('Invalid MAC');
1165          }
1166  
1167          /** @var string $m - Decrypted message */
1168          $m = ParagonIE_Sodium_Core_Util::xorStrings(
1169              ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
1170              ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
1171          );
1172  
1173          if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
1174              // We had more than 1 block, so let's continue to decrypt the rest.
1175              $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(
1176                  ParagonIE_Sodium_Core_Util::substr(
1177                      $c,
1178                      self::secretbox_xchacha20poly1305_ZEROBYTES
1179                  ),
1180                  ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8),
1181                  (string) $subkey,
1182                  ParagonIE_Sodium_Core_Util::store64_le(1)
1183              );
1184          }
1185          return $m;
1186      }
1187  
1188      /**
1189       * Detached Ed25519 signature.
1190       *
1191       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1192       *
1193       * @param string $message
1194       * @param string $sk
1195       * @return string
1196       * @throws SodiumException
1197       * @throws TypeError
1198       */
1199      public static function sign_detached($message, $sk)
1200      {
1201          return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk);
1202      }
1203  
1204      /**
1205       * Attached Ed25519 signature. (Returns a signed message.)
1206       *
1207       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1208       *
1209       * @param string $message
1210       * @param string $sk
1211       * @return string
1212       * @throws SodiumException
1213       * @throws TypeError
1214       */
1215      public static function sign($message, $sk)
1216      {
1217          return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk);
1218      }
1219  
1220      /**
1221       * Opens a signed message. If valid, returns the message.
1222       *
1223       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1224       *
1225       * @param string $signedMessage
1226       * @param string $pk
1227       * @return string
1228       * @throws SodiumException
1229       * @throws TypeError
1230       */
1231      public static function sign_open($signedMessage, $pk)
1232      {
1233          return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk);
1234      }
1235  
1236      /**
1237       * Verify a detached signature of a given message and public key.
1238       *
1239       * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1240       *
1241       * @param string $signature
1242       * @param string $message
1243       * @param string $pk
1244       * @return bool
1245       * @throws SodiumException
1246       * @throws TypeError
1247       */
1248      public static function sign_verify_detached($signature, $message, $pk)
1249      {
1250          return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk);
1251      }
1252  }


Generated: Sat Sep 21 01:00:03 2019 Cross-referenced by PHPXref 0.7.1