You are here

abstract class ParagonIE_Sodium_Crypto in Automatic Updates 8

Same name and namespace in other branches
  1. 7 vendor/paragonie/sodium_compat/src/Crypto.php \ParagonIE_Sodium_Crypto

Class ParagonIE_Sodium_Crypto

ATTENTION!

If you are using this library, you should be using ParagonIE_Sodium_Compat in your code, not this class.

Hierarchy

Expanded class hierarchy of ParagonIE_Sodium_Crypto

1 string reference to 'ParagonIE_Sodium_Crypto'
Crypto.php in vendor/paragonie/sodium_compat/src/Crypto.php

File

vendor/paragonie/sodium_compat/src/Crypto.php, line 15

View source
abstract class ParagonIE_Sodium_Crypto {
  const aead_chacha20poly1305_KEYBYTES = 32;
  const aead_chacha20poly1305_NSECBYTES = 0;
  const aead_chacha20poly1305_NPUBBYTES = 8;
  const aead_chacha20poly1305_ABYTES = 16;
  const aead_chacha20poly1305_IETF_KEYBYTES = 32;
  const aead_chacha20poly1305_IETF_NSECBYTES = 0;
  const aead_chacha20poly1305_IETF_NPUBBYTES = 12;
  const aead_chacha20poly1305_IETF_ABYTES = 16;
  const aead_xchacha20poly1305_IETF_KEYBYTES = 32;
  const aead_xchacha20poly1305_IETF_NSECBYTES = 0;
  const aead_xchacha20poly1305_IETF_NPUBBYTES = 24;
  const aead_xchacha20poly1305_IETF_ABYTES = 16;
  const box_curve25519xsalsa20poly1305_SEEDBYTES = 32;
  const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32;
  const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32;
  const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32;
  const box_curve25519xsalsa20poly1305_NONCEBYTES = 24;
  const box_curve25519xsalsa20poly1305_MACBYTES = 16;
  const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16;
  const box_curve25519xsalsa20poly1305_ZEROBYTES = 32;
  const onetimeauth_poly1305_BYTES = 16;
  const onetimeauth_poly1305_KEYBYTES = 32;
  const secretbox_xsalsa20poly1305_KEYBYTES = 32;
  const secretbox_xsalsa20poly1305_NONCEBYTES = 24;
  const secretbox_xsalsa20poly1305_MACBYTES = 16;
  const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16;
  const secretbox_xsalsa20poly1305_ZEROBYTES = 32;
  const secretbox_xchacha20poly1305_KEYBYTES = 32;
  const secretbox_xchacha20poly1305_NONCEBYTES = 24;
  const secretbox_xchacha20poly1305_MACBYTES = 16;
  const secretbox_xchacha20poly1305_BOXZEROBYTES = 16;
  const secretbox_xchacha20poly1305_ZEROBYTES = 32;
  const stream_salsa20_KEYBYTES = 32;

  /**
   * AEAD Decryption with ChaCha20-Poly1305
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $ad
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function aead_chacha20poly1305_decrypt($message = '', $ad = '', $nonce = '', $key = '') {

    /** @var int $len - Length of message (ciphertext + MAC) */
    $len = ParagonIE_Sodium_Core_Util::strlen($message);

    /** @var int  $clen - Length of ciphertext */
    $clen = $len - self::aead_chacha20poly1305_ABYTES;

    /** @var int $adlen - Length of associated data */
    $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);

    /** @var string $mac - Message authentication code */
    $mac = ParagonIE_Sodium_Core_Util::substr($message, $clen, self::aead_chacha20poly1305_ABYTES);

    /** @var string $ciphertext - The encrypted message (sans MAC) */
    $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen);

    /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
    $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(32, $nonce, $key);

    /* Recalculate the Poly1305 authentication tag (MAC): */
    $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
    try {
      ParagonIE_Sodium_Compat::memzero($block0);
    } catch (SodiumException $ex) {
      $block0 = null;
    }
    $state
      ->update($ad);
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
    $state
      ->update($ciphertext);
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
    $computed_mac = $state
      ->finish();

    /* Compare the given MAC with the recalculated MAC: */
    if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
      throw new SodiumException('Invalid MAC');
    }

    // Here, we know that the MAC is valid, so we decrypt and return the plaintext
    return ParagonIE_Sodium_Core_ChaCha20::streamXorIc($ciphertext, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1));
  }

  /**
   * AEAD Encryption with ChaCha20-Poly1305
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $ad
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function aead_chacha20poly1305_encrypt($message = '', $ad = '', $nonce = '', $key = '') {

    /** @var int $len - Length of the plaintext message */
    $len = ParagonIE_Sodium_Core_Util::strlen($message);

    /** @var int $adlen - Length of the associated data */
    $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);

    /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
    $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(32, $nonce, $key);
    $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
    try {
      ParagonIE_Sodium_Compat::memzero($block0);
    } catch (SodiumException $ex) {
      $block0 = null;
    }

    /** @var string $ciphertext - Raw encrypted data */
    $ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc($message, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1));
    $state
      ->update($ad);
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
    $state
      ->update($ciphertext);
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($len));
    return $ciphertext . $state
      ->finish();
  }

  /**
   * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $ad
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function aead_chacha20poly1305_ietf_decrypt($message = '', $ad = '', $nonce = '', $key = '') {

    /** @var int $adlen - Length of associated data */
    $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);

    /** @var int $len - Length of message (ciphertext + MAC) */
    $len = ParagonIE_Sodium_Core_Util::strlen($message);

    /** @var int  $clen - Length of ciphertext */
    $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES;

    /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
    $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $nonce, $key);

    /** @var string $mac - Message authentication code */
    $mac = ParagonIE_Sodium_Core_Util::substr($message, $len - self::aead_chacha20poly1305_IETF_ABYTES, self::aead_chacha20poly1305_IETF_ABYTES);

    /** @var string $ciphertext - The encrypted message (sans MAC) */
    $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $len - self::aead_chacha20poly1305_IETF_ABYTES);

    /* Recalculate the Poly1305 authentication tag (MAC): */
    $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
    try {
      ParagonIE_Sodium_Compat::memzero($block0);
    } catch (SodiumException $ex) {
      $block0 = null;
    }
    $state
      ->update($ad);
    $state
      ->update(str_repeat("\0", 0x10 - $adlen & 0xf));
    $state
      ->update($ciphertext);
    $state
      ->update(str_repeat("\0", 0x10 - $clen & 0xf));
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($clen));
    $computed_mac = $state
      ->finish();

    /* Compare the given MAC with the recalculated MAC: */
    if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) {
      throw new SodiumException('Invalid MAC');
    }

    // Here, we know that the MAC is valid, so we decrypt and return the plaintext
    return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($ciphertext, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1));
  }

  /**
   * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $ad
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function aead_chacha20poly1305_ietf_encrypt($message = '', $ad = '', $nonce = '', $key = '') {

    /** @var int $len - Length of the plaintext message */
    $len = ParagonIE_Sodium_Core_Util::strlen($message);

    /** @var int $adlen - Length of the associated data */
    $adlen = ParagonIE_Sodium_Core_Util::strlen($ad);

    /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
    $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $nonce, $key);
    $state = new ParagonIE_Sodium_Core_Poly1305_State($block0);
    try {
      ParagonIE_Sodium_Compat::memzero($block0);
    } catch (SodiumException $ex) {
      $block0 = null;
    }

    /** @var string $ciphertext - Raw encrypted data */
    $ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($message, $nonce, $key, ParagonIE_Sodium_Core_Util::store64_le(1));
    $state
      ->update($ad);
    $state
      ->update(str_repeat("\0", 0x10 - $adlen & 0xf));
    $state
      ->update($ciphertext);
    $state
      ->update(str_repeat("\0", 0x10 - $len & 0xf));
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($adlen));
    $state
      ->update(ParagonIE_Sodium_Core_Util::store64_le($len));
    return $ciphertext . $state
      ->finish();
  }

  /**
   * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $ad
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function aead_xchacha20poly1305_ietf_decrypt($message = '', $ad = '', $nonce = '', $key = '') {
    $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key);
    $nonceLast = "\0\0\0\0" . ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
    return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey);
  }

  /**
   * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $ad
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function aead_xchacha20poly1305_ietf_encrypt($message = '', $ad = '', $nonce = '', $key = '') {
    $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key);
    $nonceLast = "\0\0\0\0" . ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);
    return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey);
  }

  /**
   * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512)
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $key
   * @return string
   * @throws TypeError
   */
  public static function auth($message, $key) {
    return ParagonIE_Sodium_Core_Util::substr(hash_hmac('sha512', $message, $key, true), 0, 32);
  }

  /**
   * HMAC-SHA-512-256 validation. Constant-time via hash_equals().
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $mac
   * @param string $message
   * @param string $key
   * @return bool
   * @throws SodiumException
   * @throws TypeError
   */
  public static function auth_verify($mac, $message, $key) {
    return ParagonIE_Sodium_Core_Util::hashEquals($mac, self::auth($message, $key));
  }

  /**
   * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $plaintext
   * @param string $nonce
   * @param string $keypair
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box($plaintext, $nonce, $keypair) {
    $c = self::secretbox($plaintext, $nonce, self::box_beforenm(self::box_secretkey($keypair), self::box_publickey($keypair)));
    return $c;
  }

  /**
   * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $publicKey
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box_seal($message, $publicKey) {

    /** @var string $ephemeralKeypair */
    $ephemeralKeypair = self::box_keypair();

    /** @var string $ephemeralSK */
    $ephemeralSK = self::box_secretkey($ephemeralKeypair);

    /** @var string $ephemeralPK */
    $ephemeralPK = self::box_publickey($ephemeralKeypair);

    /** @var string $nonce */
    $nonce = self::generichash($ephemeralPK . $publicKey, '', 24);

    /** @var string $keypair - The combined keypair used in crypto_box() */
    $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey);

    /** @var string $ciphertext Ciphertext + MAC from crypto_box */
    $ciphertext = self::box($message, $nonce, $keypair);
    try {
      ParagonIE_Sodium_Compat::memzero($ephemeralKeypair);
      ParagonIE_Sodium_Compat::memzero($ephemeralSK);
      ParagonIE_Sodium_Compat::memzero($nonce);
    } catch (SodiumException $ex) {
      $ephemeralKeypair = null;
      $ephemeralSK = null;
      $nonce = null;
    }
    return $ephemeralPK . $ciphertext;
  }

  /**
   * Opens a message encrypted via box_seal().
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $keypair
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box_seal_open($message, $keypair) {

    /** @var string $ephemeralPK */
    $ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32);

    /** @var string $ciphertext (ciphertext + MAC) */
    $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32);

    /** @var string $secretKey */
    $secretKey = self::box_secretkey($keypair);

    /** @var string $publicKey */
    $publicKey = self::box_publickey($keypair);

    /** @var string $nonce */
    $nonce = self::generichash($ephemeralPK . $publicKey, '', 24);

    /** @var string $keypair */
    $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK);

    /** @var string $m */
    $m = self::box_open($ciphertext, $nonce, $keypair);
    try {
      ParagonIE_Sodium_Compat::memzero($secretKey);
      ParagonIE_Sodium_Compat::memzero($ephemeralPK);
      ParagonIE_Sodium_Compat::memzero($nonce);
    } catch (SodiumException $ex) {
      $secretKey = null;
      $ephemeralPK = null;
      $nonce = null;
    }
    return $m;
  }

  /**
   * Used by crypto_box() to get the crypto_secretbox() key.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $sk
   * @param string $pk
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box_beforenm($sk, $pk) {
    return ParagonIE_Sodium_Core_HSalsa20::hsalsa20(str_repeat("\0", 16), self::scalarmult($sk, $pk));
  }

  /**
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @return string
   * @throws Exception
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box_keypair() {
    $sKey = random_bytes(32);
    $pKey = self::scalarmult_base($sKey);
    return $sKey . $pKey;
  }

  /**
   * @param string $seed
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box_seed_keypair($seed) {
    $sKey = ParagonIE_Sodium_Core_Util::substr(hash('sha512', $seed, true), 0, 32);
    $pKey = self::scalarmult_base($sKey);
    return $sKey . $pKey;
  }

  /**
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $sKey
   * @param string $pKey
   * @return string
   * @throws TypeError
   */
  public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey) {
    return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) . ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32);
  }

  /**
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $keypair
   * @return string
   * @throws RangeException
   * @throws TypeError
   */
  public static function box_secretkey($keypair) {
    if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) {
      throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.');
    }
    return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32);
  }

  /**
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $keypair
   * @return string
   * @throws RangeException
   * @throws TypeError
   */
  public static function box_publickey($keypair) {
    if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
      throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.');
    }
    return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32);
  }

  /**
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $sKey
   * @return string
   * @throws RangeException
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box_publickey_from_secretkey($sKey) {
    if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
      throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.');
    }
    return self::scalarmult_base($sKey);
  }

  /**
   * Decrypt a message encrypted with box().
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $ciphertext
   * @param string $nonce
   * @param string $keypair
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function box_open($ciphertext, $nonce, $keypair) {
    return self::secretbox_open($ciphertext, $nonce, self::box_beforenm(self::box_secretkey($keypair), self::box_publickey($keypair)));
  }

  /**
   * Calculate a BLAKE2b hash.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string|null $key
   * @param int $outlen
   * @return string
   * @throws RangeException
   * @throws SodiumException
   * @throws TypeError
   */
  public static function generichash($message, $key = '', $outlen = 32) {

    // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
    ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
    $k = null;
    if (!empty($key)) {

      /** @var SplFixedArray $k */
      $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
      if ($k
        ->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
        throw new RangeException('Invalid key size');
      }
    }

    /** @var SplFixedArray $in */
    $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);

    /** @var SplFixedArray $ctx */
    $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen);
    ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in
      ->count());

    /** @var SplFixedArray $out */
    $out = new SplFixedArray($outlen);
    $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out);

    /** @var array<int, int> */
    $outArray = $out
      ->toArray();
    return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
  }

  /**
   * Finalize a BLAKE2b hashing context, returning the hash.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $ctx
   * @param int $outlen
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function generichash_final($ctx, $outlen = 32) {
    if (!is_string($ctx)) {
      throw new TypeError('Context must be a string');
    }
    $out = new SplFixedArray($outlen);

    /** @var SplFixedArray $context */
    $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);

    /** @var SplFixedArray $out */
    $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out);

    /** @var array<int, int> */
    $outArray = $out
      ->toArray();
    return ParagonIE_Sodium_Core_Util::intArrayToString($outArray);
  }

  /**
   * Initialize a hashing context for BLAKE2b.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $key
   * @param int $outputLength
   * @return string
   * @throws RangeException
   * @throws SodiumException
   * @throws TypeError
   */
  public static function generichash_init($key = '', $outputLength = 32) {

    // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
    ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
    $k = null;
    if (!empty($key)) {
      $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
      if ($k
        ->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
        throw new RangeException('Invalid key size');
      }
    }

    /** @var SplFixedArray $ctx */
    $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength);
    return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
  }

  /**
   * Initialize a hashing context for BLAKE2b.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $key
   * @param int $outputLength
   * @param string $salt
   * @param string $personal
   * @return string
   * @throws RangeException
   * @throws SodiumException
   * @throws TypeError
   */
  public static function generichash_init_salt_personal($key = '', $outputLength = 32, $salt = '', $personal = '') {

    // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
    ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();
    $k = null;
    if (!empty($key)) {
      $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key);
      if ($k
        ->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) {
        throw new RangeException('Invalid key size');
      }
    }
    if (!empty($salt)) {
      $s = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($salt);
    }
    else {
      $s = null;
    }
    if (!empty($salt)) {
      $p = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($personal);
    }
    else {
      $p = null;
    }

    /** @var SplFixedArray $ctx */
    $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength, $s, $p);
    return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx);
  }

  /**
   * Update a hashing context for BLAKE2b with $message
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $ctx
   * @param string $message
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function generichash_update($ctx, $message) {

    // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized
    ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor();

    /** @var SplFixedArray $context */
    $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx);

    /** @var SplFixedArray $in */
    $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message);
    ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in
      ->count());
    return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context);
  }

  /**
   * Libsodium's crypto_kx().
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $my_sk
   * @param string $their_pk
   * @param string $client_pk
   * @param string $server_pk
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) {
    return ParagonIE_Sodium_Compat::crypto_generichash(ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) . $client_pk . $server_pk);
  }

  /**
   * ECDH over Curve25519
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $sKey
   * @param string $pKey
   * @return string
   *
   * @throws SodiumException
   * @throws TypeError
   */
  public static function scalarmult($sKey, $pKey) {
    $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
    self::scalarmult_throw_if_zero($q);
    return $q;
  }

  /**
   * ECDH over Curve25519, using the basepoint.
   * Used to get a secret key from a public key.
   *
   * @param string $secret
   * @return string
   *
   * @throws SodiumException
   * @throws TypeError
   */
  public static function scalarmult_base($secret) {
    $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
    self::scalarmult_throw_if_zero($q);
    return $q;
  }

  /**
   * This throws an Error if a zero public key was passed to the function.
   *
   * @param string $q
   * @return void
   * @throws SodiumException
   * @throws TypeError
   */
  protected static function scalarmult_throw_if_zero($q) {
    $d = 0;
    for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
      $d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]);
    }

    /* branch-free variant of === 0 */
    if (-(1 & $d - 1 >> 8)) {
      throw new SodiumException('Zero public key is not allowed');
    }
  }

  /**
   * XSalsa20-Poly1305 authenticated symmetric-key encryption.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $plaintext
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function secretbox($plaintext, $nonce, $key) {

    /** @var string $subkey */
    $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);

    /** @var string $block0 */
    $block0 = str_repeat("\0", 32);

    /** @var int $mlen - Length of the plaintext message */
    $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
    $mlen0 = $mlen;
    if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
      $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
    }
    $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);

    /** @var string $block0 */
    $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor($block0, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey);

    /** @var string $c */
    $c = ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES);
    if ($mlen > $mlen0) {
      $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(ParagonIE_Sodium_Core_Util::substr($plaintext, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1, $subkey);
    }
    $state = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_Util::substr($block0, 0, self::onetimeauth_poly1305_KEYBYTES));
    try {
      ParagonIE_Sodium_Compat::memzero($block0);
      ParagonIE_Sodium_Compat::memzero($subkey);
    } catch (SodiumException $ex) {
      $block0 = null;
      $subkey = null;
    }
    $state
      ->update($c);

    /** @var string $c - MAC || ciphertext */
    $c = $state
      ->finish() . $c;
    unset($state);
    return $c;
  }

  /**
   * Decrypt a ciphertext generated via secretbox().
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $ciphertext
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function secretbox_open($ciphertext, $nonce, $key) {

    /** @var string $mac */
    $mac = ParagonIE_Sodium_Core_Util::substr($ciphertext, 0, self::secretbox_xsalsa20poly1305_MACBYTES);

    /** @var string $c */
    $c = ParagonIE_Sodium_Core_Util::substr($ciphertext, self::secretbox_xsalsa20poly1305_MACBYTES);

    /** @var int $clen */
    $clen = ParagonIE_Sodium_Core_Util::strlen($c);

    /** @var string $subkey */
    $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key);

    /** @var string $block0 */
    $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20(64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey);
    $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify($mac, $c, ParagonIE_Sodium_Core_Util::substr($block0, 0, 32));
    if (!$verified) {
      try {
        ParagonIE_Sodium_Compat::memzero($subkey);
      } catch (SodiumException $ex) {
        $subkey = null;
      }
      throw new SodiumException('Invalid MAC');
    }

    /** @var string $m - Decrypted message */
    $m = ParagonIE_Sodium_Core_Util::xorStrings(ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES));
    if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {

      // We had more than 1 block, so let's continue to decrypt the rest.
      $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic(ParagonIE_Sodium_Core_Util::substr($c, self::secretbox_xsalsa20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), 1, (string) $subkey);
    }
    return $m;
  }

  /**
   * XChaCha20-Poly1305 authenticated symmetric-key encryption.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $plaintext
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) {

    /** @var string $subkey */
    $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), $key);
    $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8);

    /** @var string $block0 */
    $block0 = str_repeat("\0", 32);

    /** @var int $mlen - Length of the plaintext message */
    $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext);
    $mlen0 = $mlen;
    if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
      $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
    }
    $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0);

    /** @var string $block0 */
    $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc($block0, $nonceLast, $subkey);

    /** @var string $c */
    $c = ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES);
    if ($mlen > $mlen0) {
      $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(ParagonIE_Sodium_Core_Util::substr($plaintext, self::secretbox_xchacha20poly1305_ZEROBYTES), $nonceLast, $subkey, ParagonIE_Sodium_Core_Util::store64_le(1));
    }
    $state = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_Util::substr($block0, 0, self::onetimeauth_poly1305_KEYBYTES));
    try {
      ParagonIE_Sodium_Compat::memzero($block0);
      ParagonIE_Sodium_Compat::memzero($subkey);
    } catch (SodiumException $ex) {
      $block0 = null;
      $subkey = null;
    }
    $state
      ->update($c);

    /** @var string $c - MAC || ciphertext */
    $c = $state
      ->finish() . $c;
    unset($state);
    return $c;
  }

  /**
   * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $ciphertext
   * @param string $nonce
   * @param string $key
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) {

    /** @var string $mac */
    $mac = ParagonIE_Sodium_Core_Util::substr($ciphertext, 0, self::secretbox_xchacha20poly1305_MACBYTES);

    /** @var string $c */
    $c = ParagonIE_Sodium_Core_Util::substr($ciphertext, self::secretbox_xchacha20poly1305_MACBYTES);

    /** @var int $clen */
    $clen = ParagonIE_Sodium_Core_Util::strlen($c);

    /** @var string $subkey */
    $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key);

    /** @var string $block0 */
    $block0 = ParagonIE_Sodium_Core_ChaCha20::stream(64, ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), $subkey);
    $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify($mac, $c, ParagonIE_Sodium_Core_Util::substr($block0, 0, 32));
    if (!$verified) {
      try {
        ParagonIE_Sodium_Compat::memzero($subkey);
      } catch (SodiumException $ex) {
        $subkey = null;
      }
      throw new SodiumException('Invalid MAC');
    }

    /** @var string $m - Decrypted message */
    $m = ParagonIE_Sodium_Core_Util::xorStrings(ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES));
    if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {

      // We had more than 1 block, so let's continue to decrypt the rest.
      $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc(ParagonIE_Sodium_Core_Util::substr($c, self::secretbox_xchacha20poly1305_ZEROBYTES), ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), (string) $subkey, ParagonIE_Sodium_Core_Util::store64_le(1));
    }
    return $m;
  }

  /**
   * @param string $key
   * @return array<int, string> Returns a state and a header.
   * @throws Exception
   * @throws SodiumException
   */
  public static function secretstream_xchacha20poly1305_init_push($key) {

    # randombytes_buf(out, crypto_secretstream_xchacha20poly1305_HEADERBYTES);
    $out = random_bytes(24);

    # crypto_core_hchacha20(state->k, out, k, NULL);
    $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20($out, $key);
    $state = new ParagonIE_Sodium_Core_SecretStream_State($subkey, ParagonIE_Sodium_Core_Util::substr($out, 16, 8) . str_repeat("\0", 4));

    # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
    $state
      ->counterReset();

    # memcpy(STATE_INONCE(state), out + crypto_core_hchacha20_INPUTBYTES,

    #        crypto_secretstream_xchacha20poly1305_INONCEBYTES);

    # memset(state->_pad, 0, sizeof state->_pad);
    return array(
      $state
        ->toString(),
      $out,
    );
  }

  /**
   * @param string $key
   * @param string $header
   * @return string Returns a state.
   * @throws Exception
   */
  public static function secretstream_xchacha20poly1305_init_pull($key, $header) {

    # crypto_core_hchacha20(state->k, in, k, NULL);
    $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20(ParagonIE_Sodium_Core_Util::substr($header, 0, 16), $key);
    $state = new ParagonIE_Sodium_Core_SecretStream_State($subkey, ParagonIE_Sodium_Core_Util::substr($header, 16));
    $state
      ->counterReset();

    # memcpy(STATE_INONCE(state), in + crypto_core_hchacha20_INPUTBYTES,

    #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);

    # memset(state->_pad, 0, sizeof state->_pad);

    # return 0;
    return $state
      ->toString();
  }

  /**
   * @param string $state
   * @param string $msg
   * @param string $aad
   * @param int $tag
   * @return string
   * @throws SodiumException
   */
  public static function secretstream_xchacha20poly1305_push(&$state, $msg, $aad = '', $tag = 0) {
    $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);

    # crypto_onetimeauth_poly1305_state poly1305_state;

    # unsigned char                     block[64U];

    # unsigned char                     slen[8U];

    # unsigned char                    *c;

    # unsigned char                    *mac;
    $msglen = ParagonIE_Sodium_Core_Util::strlen($msg);
    $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);
    if ($msglen + 63 >> 6 > 0xfffffffe) {
      throw new SodiumException('message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes');
    }

    # if (outlen_p != NULL) {

    #     *outlen_p = 0U;

    # }

    # if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {

    #     sodium_misuse();

    # }

    # crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);

    # crypto_onetimeauth_poly1305_init(&poly1305_state, block);

    # sodium_memzero(block, sizeof block);
    $auth = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st
      ->getCombinedNonce(), $st
      ->getKey()));

    # crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
    $auth
      ->update($aad);

    # crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,

    #     (0x10 - adlen) & 0xf);
    $auth
      ->update(str_repeat("\0", 0x10 - $aadlen & 0xf));

    # memset(block, 0, sizeof block);

    # block[0] = tag;

    # crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,

    #                                    state->nonce, 1U, state->k);
    $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(ParagonIE_Sodium_Core_Util::intToChr($tag) . str_repeat("\0", 63), $st
      ->getCombinedNonce(), $st
      ->getKey(), ParagonIE_Sodium_Core_Util::store64_le(1));

    # crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
    $auth
      ->update($block);

    # out[0] = block[0];
    $out = $block[0];

    # c = out + (sizeof tag);

    # crypto_stream_chacha20_ietf_xor_ic(c, m, mlen, state->nonce, 2U, state->k);
    $cipher = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($msg, $st
      ->getCombinedNonce(), $st
      ->getKey(), ParagonIE_Sodium_Core_Util::store64_le(2));

    # crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
    $auth
      ->update($cipher);
    $out .= $cipher;
    unset($cipher);

    # crypto_onetimeauth_poly1305_update

    # (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
    $auth
      ->update(str_repeat("\0", 0x10 - 64 + $msglen & 0xf));

    # STORE64_LE(slen, (uint64_t) adlen);
    $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);

    # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
    $auth
      ->update($slen);

    # STORE64_LE(slen, (sizeof block) + mlen);
    $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);

    # crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
    $auth
      ->update($slen);

    # mac = c + mlen;

    # crypto_onetimeauth_poly1305_final(&poly1305_state, mac);
    $mac = $auth
      ->finish();
    $out .= $mac;

    # sodium_memzero(&poly1305_state, sizeof poly1305_state);
    unset($auth);

    # XOR_BUF(STATE_INONCE(state), mac,

    #     crypto_secretstream_xchacha20poly1305_INONCEBYTES);
    $st
      ->xorNonce($mac);

    # sodium_increment(STATE_COUNTER(state),

    #     crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
    $st
      ->incrementCounter();

    // Overwrite by reference:
    $state = $st
      ->toString();

    /** @var bool $rekey */
    $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;

    # if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||

    #     sodium_is_zero(STATE_COUNTER(state),

    #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {

    #     crypto_secretstream_xchacha20poly1305_rekey(state);

    # }
    if ($rekey || $st
      ->needsRekey()) {

      // DO REKEY
      self::secretstream_xchacha20poly1305_rekey($state);
    }

    # if (outlen_p != NULL) {

    #     *outlen_p = crypto_secretstream_xchacha20poly1305_ABYTES + mlen;

    # }
    return $out;
  }

  /**
   * @param string $state
   * @param string $cipher
   * @param string $aad
   * @return bool|array{0: string, 1: int}
   * @throws SodiumException
   */
  public static function secretstream_xchacha20poly1305_pull(&$state, $cipher, $aad = '') {
    $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);
    $cipherlen = ParagonIE_Sodium_Core_Util::strlen($cipher);

    #     mlen = inlen - crypto_secretstream_xchacha20poly1305_ABYTES;
    $msglen = $cipherlen - ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES;
    $aadlen = ParagonIE_Sodium_Core_Util::strlen($aad);

    #     if (mlen > crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX) {

    #         sodium_misuse();

    #     }
    if ($msglen + 63 >> 6 > 0xfffffffe) {
      throw new SodiumException('message cannot be larger than SODIUM_CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX bytes');
    }

    #     crypto_stream_chacha20_ietf(block, sizeof block, state->nonce, state->k);

    #     crypto_onetimeauth_poly1305_init(&poly1305_state, block);

    #     sodium_memzero(block, sizeof block);
    $auth = new ParagonIE_Sodium_Core_Poly1305_State(ParagonIE_Sodium_Core_ChaCha20::ietfStream(32, $st
      ->getCombinedNonce(), $st
      ->getKey()));

    #     crypto_onetimeauth_poly1305_update(&poly1305_state, ad, adlen);
    $auth
      ->update($aad);

    #     crypto_onetimeauth_poly1305_update(&poly1305_state, _pad0,

    #         (0x10 - adlen) & 0xf);
    $auth
      ->update(str_repeat("\0", 0x10 - $aadlen & 0xf));

    #     memset(block, 0, sizeof block);

    #     block[0] = in[0];

    #     crypto_stream_chacha20_ietf_xor_ic(block, block, sizeof block,

    #                                        state->nonce, 1U, state->k);
    $block = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($cipher[0] . str_repeat("\0", 63), $st
      ->getCombinedNonce(), $st
      ->getKey(), ParagonIE_Sodium_Core_Util::store64_le(1));

    #     tag = block[0];

    #     block[0] = in[0];

    #     crypto_onetimeauth_poly1305_update(&poly1305_state, block, sizeof block);
    $tag = ParagonIE_Sodium_Core_Util::chrToInt($block[0]);
    $block[0] = $cipher[0];
    $auth
      ->update($block);

    #     c = in + (sizeof tag);

    #     crypto_onetimeauth_poly1305_update(&poly1305_state, c, mlen);
    $auth
      ->update(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen));

    #     crypto_onetimeauth_poly1305_update

    #     (&poly1305_state, _pad0, (0x10 - (sizeof block) + mlen) & 0xf);
    $auth
      ->update(str_repeat("\0", 0x10 - 64 + $msglen & 0xf));

    #     STORE64_LE(slen, (uint64_t) adlen);

    #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
    $slen = ParagonIE_Sodium_Core_Util::store64_le($aadlen);
    $auth
      ->update($slen);

    #     STORE64_LE(slen, (sizeof block) + mlen);

    #     crypto_onetimeauth_poly1305_update(&poly1305_state, slen, sizeof slen);
    $slen = ParagonIE_Sodium_Core_Util::store64_le(64 + $msglen);
    $auth
      ->update($slen);

    #     crypto_onetimeauth_poly1305_final(&poly1305_state, mac);

    #     sodium_memzero(&poly1305_state, sizeof poly1305_state);
    $mac = $auth
      ->finish();

    #     stored_mac = c + mlen;

    #     if (sodium_memcmp(mac, stored_mac, sizeof mac) != 0) {

    #     sodium_memzero(mac, sizeof mac);

    #         return -1;

    #     }
    $stored = ParagonIE_Sodium_Core_Util::substr($cipher, $msglen + 1, 16);
    if (!ParagonIE_Sodium_Core_Util::hashEquals($mac, $stored)) {
      return false;
    }

    #     crypto_stream_chacha20_ietf_xor_ic(m, c, mlen, state->nonce, 2U, state->k);
    $out = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc(ParagonIE_Sodium_Core_Util::substr($cipher, 1, $msglen), $st
      ->getCombinedNonce(), $st
      ->getKey(), ParagonIE_Sodium_Core_Util::store64_le(2));

    #     XOR_BUF(STATE_INONCE(state), mac,

    #         crypto_secretstream_xchacha20poly1305_INONCEBYTES);
    $st
      ->xorNonce($mac);

    #     sodium_increment(STATE_COUNTER(state),

    #         crypto_secretstream_xchacha20poly1305_COUNTERBYTES);
    $st
      ->incrementCounter();

    #     if ((tag & crypto_secretstream_xchacha20poly1305_TAG_REKEY) != 0 ||

    #         sodium_is_zero(STATE_COUNTER(state),

    #             crypto_secretstream_xchacha20poly1305_COUNTERBYTES)) {

    #         crypto_secretstream_xchacha20poly1305_rekey(state);

    #     }

    // Overwrite by reference:
    $state = $st
      ->toString();

    /** @var bool $rekey */
    $rekey = ($tag & ParagonIE_Sodium_Compat::CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY) !== 0;
    if ($rekey || $st
      ->needsRekey()) {

      // DO REKEY
      self::secretstream_xchacha20poly1305_rekey($state);
    }
    return array(
      $out,
      $tag,
    );
  }

  /**
   * @param string $state
   * @return void
   * @throws SodiumException
   */
  public static function secretstream_xchacha20poly1305_rekey(&$state) {
    $st = ParagonIE_Sodium_Core_SecretStream_State::fromString($state);

    # unsigned char new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES +

    # crypto_secretstream_xchacha20poly1305_INONCEBYTES];

    # size_t        i;

    # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {

    #     new_key_and_inonce[i] = state->k[i];

    # }
    $new_key_and_inonce = $st
      ->getKey();

    # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {

    #     new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i] =

    #         STATE_INONCE(state)[i];

    # }
    $new_key_and_inonce .= ParagonIE_Sodium_Core_Util::substR($st
      ->getNonce(), 0, 8);

    # crypto_stream_chacha20_ietf_xor(new_key_and_inonce, new_key_and_inonce,

    #                                 sizeof new_key_and_inonce,

    #                                 state->nonce, state->k);
    $st
      ->rekey(ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc($new_key_and_inonce, $st
      ->getCombinedNonce(), $st
      ->getKey(), ParagonIE_Sodium_Core_Util::store64_le(0)));

    # for (i = 0U; i < crypto_stream_chacha20_ietf_KEYBYTES; i++) {

    #     state->k[i] = new_key_and_inonce[i];

    # }

    # for (i = 0U; i < crypto_secretstream_xchacha20poly1305_INONCEBYTES; i++) {

    #     STATE_INONCE(state)[i] =

    #          new_key_and_inonce[crypto_stream_chacha20_ietf_KEYBYTES + i];

    # }

    # _crypto_secretstream_xchacha20poly1305_counter_reset(state);
    $st
      ->counterReset();
    $state = $st
      ->toString();
  }

  /**
   * Detached Ed25519 signature.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $sk
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function sign_detached($message, $sk) {
    return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk);
  }

  /**
   * Attached Ed25519 signature. (Returns a signed message.)
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $message
   * @param string $sk
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function sign($message, $sk) {
    return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk);
  }

  /**
   * Opens a signed message. If valid, returns the message.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $signedMessage
   * @param string $pk
   * @return string
   * @throws SodiumException
   * @throws TypeError
   */
  public static function sign_open($signedMessage, $pk) {
    return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk);
  }

  /**
   * Verify a detached signature of a given message and public key.
   *
   * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
   *
   * @param string $signature
   * @param string $message
   * @param string $pk
   * @return bool
   * @throws SodiumException
   * @throws TypeError
   */
  public static function sign_verify_detached($signature, $message, $pk) {
    return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ABYTES constant
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_decrypt public static function AEAD Decryption with ChaCha20-Poly1305
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_encrypt public static function AEAD Encryption with ChaCha20-Poly1305
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_IETF_ABYTES constant
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_decrypt public static function AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_ietf_encrypt public static function AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_IETF_KEYBYTES constant
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_IETF_NPUBBYTES constant
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_IETF_NSECBYTES constant
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_KEYBYTES constant
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_NPUBBYTES constant
ParagonIE_Sodium_Crypto::aead_chacha20poly1305_NSECBYTES constant
ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_IETF_ABYTES constant
ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_decrypt public static function AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_ietf_encrypt public static function AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce)
ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_IETF_KEYBYTES constant
ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_IETF_NPUBBYTES constant
ParagonIE_Sodium_Crypto::aead_xchacha20poly1305_IETF_NSECBYTES constant
ParagonIE_Sodium_Crypto::auth public static function HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512)
ParagonIE_Sodium_Crypto::auth_verify public static function HMAC-SHA-512-256 validation. Constant-time via hash_equals().
ParagonIE_Sodium_Crypto::box public static function X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption
ParagonIE_Sodium_Crypto::box_beforenm public static function Used by crypto_box() to get the crypto_secretbox() key.
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_BEFORENMBYTES constant
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_BOXZEROBYTES constant
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_MACBYTES constant
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_NONCEBYTES constant
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_PUBLICKEYBYTES constant
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_SECRETKEYBYTES constant
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_SEEDBYTES constant
ParagonIE_Sodium_Crypto::box_curve25519xsalsa20poly1305_ZEROBYTES constant
ParagonIE_Sodium_Crypto::box_keypair public static function @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
ParagonIE_Sodium_Crypto::box_keypair_from_secretkey_and_publickey public static function @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
ParagonIE_Sodium_Crypto::box_open public static function Decrypt a message encrypted with box().
ParagonIE_Sodium_Crypto::box_publickey public static function @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
ParagonIE_Sodium_Crypto::box_publickey_from_secretkey public static function @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
ParagonIE_Sodium_Crypto::box_seal public static function X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair.
ParagonIE_Sodium_Crypto::box_seal_open public static function Opens a message encrypted via box_seal().
ParagonIE_Sodium_Crypto::box_secretkey public static function @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
ParagonIE_Sodium_Crypto::box_seed_keypair public static function
ParagonIE_Sodium_Crypto::generichash public static function Calculate a BLAKE2b hash.
ParagonIE_Sodium_Crypto::generichash_final public static function Finalize a BLAKE2b hashing context, returning the hash.
ParagonIE_Sodium_Crypto::generichash_init public static function Initialize a hashing context for BLAKE2b.
ParagonIE_Sodium_Crypto::generichash_init_salt_personal public static function Initialize a hashing context for BLAKE2b.
ParagonIE_Sodium_Crypto::generichash_update public static function Update a hashing context for BLAKE2b with $message
ParagonIE_Sodium_Crypto::keyExchange public static function Libsodium's crypto_kx().
ParagonIE_Sodium_Crypto::onetimeauth_poly1305_BYTES constant
ParagonIE_Sodium_Crypto::onetimeauth_poly1305_KEYBYTES constant
ParagonIE_Sodium_Crypto::scalarmult public static function ECDH over Curve25519
ParagonIE_Sodium_Crypto::scalarmult_base public static function ECDH over Curve25519, using the basepoint. Used to get a secret key from a public key.
ParagonIE_Sodium_Crypto::scalarmult_throw_if_zero protected static function This throws an Error if a zero public key was passed to the function.
ParagonIE_Sodium_Crypto::secretbox public static function XSalsa20-Poly1305 authenticated symmetric-key encryption.
ParagonIE_Sodium_Crypto::secretbox_open public static function Decrypt a ciphertext generated via secretbox().
ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305 public static function XChaCha20-Poly1305 authenticated symmetric-key encryption.
ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_BOXZEROBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_KEYBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_MACBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_NONCEBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_open public static function Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
ParagonIE_Sodium_Crypto::secretbox_xchacha20poly1305_ZEROBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_BOXZEROBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_KEYBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_MACBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_NONCEBYTES constant
ParagonIE_Sodium_Crypto::secretbox_xsalsa20poly1305_ZEROBYTES constant
ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_pull public static function
ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_init_push public static function
ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_pull public static function
ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_push public static function
ParagonIE_Sodium_Crypto::secretstream_xchacha20poly1305_rekey public static function
ParagonIE_Sodium_Crypto::sign public static function Attached Ed25519 signature. (Returns a signed message.)
ParagonIE_Sodium_Crypto::sign_detached public static function Detached Ed25519 signature.
ParagonIE_Sodium_Crypto::sign_open public static function Opens a signed message. If valid, returns the message.
ParagonIE_Sodium_Crypto::sign_verify_detached public static function Verify a detached signature of a given message and public key.
ParagonIE_Sodium_Crypto::stream_salsa20_KEYBYTES constant